我有一个关于如何使用Dispose()和析构函数的问题.阅读一些文章和MSDN 文档,这似乎是实现Dispose()和析构函数的推荐方法.
但是我对这个实现有两个问题,你可以在下面看到:
class Testing : IDisposable
{
bool _disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!_disposed) // only dispose once!
{
if (disposing)
{
// Not in destructor, OK to reference other objects
}
// perform cleanup for this object
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
// tell the GC not to finalize
GC.SuppressFinalize(this);
}
~Testing()
{
Dispose(false);
}
}
Run Code Online (Sandbox Code Playgroud)
当程序员使用using或调用Dispose()显式时,我们的类正在调用GC.SupressFinalize(this).我的问题是:
由于几乎没有保证什么时候,甚至终结器运行和终结器几乎被认为是现在的气味 - 有没有办法说服JVM完全跳过所有的终结过程?
我问,因为我们有一个庞大的应用程序,当移动到一个较新的JVM(不确定在这个阶段)是什么看起来非常像终结者的已知问题(抛出异常,因此非常慢的GC) .
添加
有关Java内存泄漏故障排除的讨论:终结?建议在终结器中抛出异常时出现主要问题,因为这会大大减慢终结过程.
当内存变低并且堆转储分析显示大量Finalizer对象(超过10,000,000)时,我的问题显示为急剧减速- 向我建议减速可能是他们的错,因为他们正在推迟GC.显然我可能是错的.
我没有权力要求重构.
基于我的所有阅读,应该有一个GC线程来调用所有终结器.现在,问题是这个"一个"线程的范围是什么 - 每个进程或每个应用程序域,因为域的整个意图是在一个进程空间中分离并创建"独立"的不同应用程序.
我在这里读到:
如果在终结器中发生未处理的异常,则CLR的执行线程将吞下该异常,将终结器视为正常完成,将其从可释放队列中移除并移至下一个条目.
更严重的是,如果你的终结器由于某种原因没有退出会发生什么,例如它会阻塞,等待一个永远不会发生的情况.在这种情况下,终结器线程将被挂起,因此不再有可终结的对象将被垃圾收集.您应该非常了解这种情况,并坚持编写最简单的代码来释放终结器中的非托管资源.
另一个考虑因素是应用程序关闭期间发生 当程序关闭时,垃圾收集器将尽力调用所有可终结对象的终结器,但有一些限制:
在关闭期间,可终结对象不会升级到更高的堆生成.
任何单个终结器最多只能执行2秒; 如果它需要更长时间,它将被杀死.
所有终结器的执行时间最长为40秒; 如果任何终结器仍在执行,或者此时待决,整个过程将被突然终止.
太多帖子(甚至官方文档)滥用术语"应用程序","进程"和"应用程序域" - 他们中的大多数甚至假设它们是相同的,因为通常应用程序在单个进程中在单个应用程序域中运行.这种滥用使得所有这些文档难以阅读,甚至没有用处.
因此,我的问题假设多个应用程序,每个应用程序在单个进程中在单独的应用程序域中运行.
所有这些应用程序是否共享相同的GC和终结器线程?上面的文章中描述的问题(挂起终结器线程)是否会影响该进程中的所有应用程序?如果是 - 是否有解决方法(除了不使用不良应用程序),就像以某种方式发现终结器线程并发送它Thread.Abort?
以上都是因为我遇到了类似的问题.我的应用程序在单独的应用程序域中运行,作为第三方软件(Outlook)的插件.由于各种原因,我需要调用GC.Collect和GC.WaitForPendingFinalizers来完全释放COM引用(对于Office/Outlook,通常的互操作例程是不够的),当一个特定的其他第三方插件运行时,我的GC.WaitForPendingFinalizers将永远挂起,所以我怀疑第三方添加的"坏"终结器.我无法控制替换/删除添加(客户端的要求),因此我必须自己弄清楚如何使它们共存.
我假设在终止调试时(例如通过点击"停止"按钮,或按Shift + F5),任何实现终结器或IDisposable的类都将被处理掉.
我有一些实现IDisposable的类.当应用程序从调试器退出时(或从生产中崩溃),我想做(尝试)并执行一些操作.现在,Dispose()似乎没有被调用,也没有终结器IDisposable
有没有办法做到这一点?
如果Java finalize方法中存在无限循环或死锁,那么Finalizer线程会做什么.
在Java中,覆盖该finalize方法会得到一个糟糕的说唱,虽然我不明白为什么.类似于FileInputStream使用它来确保close在Java 8和Java 10中调用.然而,Java 9引入java.lang.ref.Cleaner了使用PhantomReference机制而不是GC终结.起初,我认为这只是向第三方类添加finalization的一种方式.但是,其javadoc中给出的示例显示了一个可以使用终结器轻松重写的用例.
我应该finalize用Cleaner 重写我的所有方法吗?(当然,我没有很多.只是一些使用OS资源的类,特别是对于CUDA互操作.)
据我所知,Cleaner(通过PhantomReference)避免了一些危险finalizer.特别是,您无法访问已清理的对象,因此您无法复活它或其任何字段.
然而,这是我能看到的唯一优势.清洁工也是非平凡的.事实上,它和终结都使用了ReferenceQueue!(难道你不喜欢阅读JDK是多么容易吗?)它比完成更快吗?它是否避免等待两个GC?如果许多对象排队等待清理,它会避免堆耗尽吗?(所有这些的答案在我看来都不是.)
最后,实际上没有任何保证阻止您在清理操作中引用目标对象.小心阅读长API注意!如果你最终引用了这个对象,那么整个机制将会默默地中断,而不像终结总是试图跛行.最后,虽然终结线程由JVM管理,但创建和保存Cleaner线程是您自己的责任.
出于某种原因,FXCop似乎认为我应该在Dispose中调用GC.SuppressFinalize,无论我是否有终结器.
我错过了什么吗?是否有理由在没有定义终结器的对象上调用GC.SuppressFinalize?
我正在翻译一个将非托管库包装到F#的C#类.我遇到了重写后面的析构函数这个看似简单的问题.
class Wrapper {
// P/Invoke ellided
private SomeType x;
public Wrapper() {
x = new SomeType();
Begin();
}
public ~Wrapper() {
End();
}
Run Code Online (Sandbox Code Playgroud)
我现在简化的F#代码如下:
type Wrapper() =
[<Literal>]
static let wrappedDll = "Library.dll"
[<DllImport(wrappedDll , EntryPoint = "Begin")>]
static extern void Begin()
[<DllImport(wrappedDll , EntryPoint = "End")>]
static extern void End()
let x = new SomeType()
do
Begin()
Run Code Online (Sandbox Code Playgroud)
如何修改此F#代码以具有相同的行为?我对F#析构函数的搜索没有出现在我的书籍或网络上.
谢谢.
"终结者监护人"[Effective Java,第30页]如何运作?
你用过它们了吗?它解决了任何具体问题吗?
我有一个看似泄漏的行为不当的应用程序.在简要的剖析器调查之后,大多数内存(80%)由java.lang.ref.Finalizer实例保存.我怀疑终结器无法运行.
这种情况的常见原因似乎是终结者抛出的异常.但是,类的finalize方法的javadoc Object(例如,参见这里)似乎与自己相矛盾:它说明了
如果finalize方法抛出未捕获的异常,则忽略该异常并终止该对象的终止.
但后来,它也说明了这一点
finalize方法抛出的任何异常都会导致暂停此对象的终结,但会被忽略.
我应该相信什么(即终止或不结束?),您是否有任何关于如何调查此类明显泄漏的提示?
谢谢