假设这是传统的Dispose模式(取自devx,但在许多网站上看到)
class Test : IDisposable
{
  private bool isDisposed = false;
  ~Test()
  {
    Dispose(false);
  }
  protected void Dispose(bool disposing)
  {
    if (disposing)
    {
      // Code to dispose the managed resources of the class
    }
    // Code to dispose the un-managed resources of the class
    isDisposed = true;
  }
  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }
}
我不明白为什么打电话GC.SupressFinalize(this).这需要我编写自己的托管资源处理,包括清空我的引用?我必须承认,我有点迷茫.有人会对这种模式有所了解吗?
理想情况下,我只想处理我的非托管资源,让GC自己进行托管收集.
实际上,我甚至不知道为什么我们指定一个终结器.在任何情况下,编码人员都应该自己处理,现在不应该他?如果这只是一个后备机制,我会删除它.
public class Test {
    public static void main(String[] args) throws Exception {
        A aObject = new A();
        ReferenceQueue<A> queue = new ReferenceQueue<>();
        PhantomReference<A> weak = new PhantomReference<>(aObject, queue);
        aObject = null;
        System.gc();
        TimeUnit.SECONDS.sleep(1);
        System.out.println(queue.poll());
    }
}
class A{
    @Override
    protected void finalize() throws Throwable {
        // TODO Auto-generated method stub
        super.finalize();
        System.out.println("finalize");
    }
}
结果是:
finalize
null
但如果我删除A类中的finalize方法,结果是:
java.lang.ref.PhantomReference@5b2c9e5d
所以,结果显示当我覆盖finalize方法时,弱对象没有被放入引用队列,那是因为aObject复活了吗?但我在finalize方法中没有做任何事情
幻影参考用于验尸操作.Java规范规定,在清除幻像引用本身之前,不会释放幻像引用的对象.
我的问题是:此功能(对象未解除分配)的用途是什么?
(我提出的唯一想法是允许本机代码对对象进行事后清理,但这并不是很有说服力).
我正在使用一个搜索库,建议保持搜索句柄对象打开,这样可以使查询缓存受益.随着时间的推移,我发现缓存容易变得臃肿(几百兆并且不断增长),并且OOM开始启动.没有办法强制执行此缓存的限制,也没有计划它可以使用多少内存.所以我增加了Xmx限制,但这只是问题的临时解决方案.
最后,我想使这个对象所指的java.lang.ref.SoftReference.因此,如果系统在可用内存上运行不足,它将让对象运行并根据需要创建一个新对象.这会在新开始后降低一些速度,但这比击中OOM要好得多.
我看到的关于SoftReferences的唯一问题是没有干净的方式让他们的指示物最终确定.在我的情况下,在销毁搜索句柄之前我需要关闭它,否则系统可能会用完文件描述符.显然,我可以将这个句柄包装到另一个对象中,在其上写一个终结器(或挂钩到ReferenceQueue/PhantomReference)然后松开.但是,嘿,这个星球上的每篇文章都建议不要使用终结器,特别是 - 针对释放文件句柄的终结器(例如Effective Java ed.II,第27页).
所以我有些困惑.我应该小心地忽略所有这些建议并继续.否则,还有其他可行的替代方案吗?提前致谢.
编辑#1:根据Tom Hawtin的建议测试了一些代码后添加了下面的文字.对我来说,似乎任何一个建议都没有用,或者我错过了一些东西.这是代码:
class Bloat {  // just a heap filler really
   private double a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z;
   private final int ii;
   public Bloat(final int ii) {
      this.ii = ii;
   }
}
// as recommended by Tom Hawtin
class MyReference<T> extends SoftReference<T> {
   private final T hardRef;
   MyReference(T referent, ReferenceQueue<? super T> q) {
      super(referent, q);
      this.hardRef = referent;
   }
}
//...meanwhile, somewhere in the neighbouring galaxy...
{
   ReferenceQueue<Bloat> …当我的类包含套接字和事件时,如何实现Dispose模式?
它应该是这样的吗?
class MyClass
{
   Socket m_ListenerSocket = new Socket();
   book m_Disposed=false;
   public void Dispose()
   {
      Dispose(true);
      GC.SuppressFinalize(this);
   }
   private void Dispose(bool isDisposing)
   {
      if (!m_Disposed)
      {
          if (isDisposing)
          {
              if (m_ListenerSocket != null)
              {
                   m_ListenerSocket.Dispose();
                   innerClass.Notify -= Notify;
              }
          }
        //finalized unmanged code here
        m_Disposed = true;
      }
  }
  ~MyClass()
  {
       Dispose(false);
  }
}
我很困惑...套接字类是"托管代码c#版本的winSock"?因此,如果用户名为dispose("isDisposing IS为true"),它应该被释放,那么事件处理程序呢?
所以在最终评论部分应该只释放Inptr对象?谢谢.
如果你是幸运一些类实现AutoClosable但有时你必须要小心,检查现有的方法要注意有一个close,destroy或shutdown方法(或什么都笔者决定将它命名).
这是Java中资源泄漏的主要来源.
我和一位同事正在讨论这个问题,并且也想知道:为什么这不能以某种方式自动化?
从理论上讲,您可以使用finalize这种情况,但不建议这样做.那么为什么没有办法只使用其中一些可关闭的资源并让GC在实例不再可用时自动关闭它们而不必记住明确地写一些close处理代码(比如try ...)?
这是因为在GC启动之前系统可能资源不足(文件描述符,...)?
注意:我尽可能使用autoclose并使用FindBugs(+ FB contrib)检查我的代码是否存在内存泄漏,但我仍然想知道......
同样感兴趣(如答案中所讨论的):最终确定的弃用.
在我们的服务器上,我们开始遇到问题OutOfMemoryError.我们使用Eclipse Memory Analysis分析了堆转储,并发现许多对象都要进行最终化(约占堆的2/3):

我们发现,它可能是一些finalize()方法阻塞.我发现了几个关于这个问题的bug报告(这里或这里),它总是在Finalizer线程堆栈中表现出来,它在某个地方被阻塞了.但在我们的例子中,这个帖子是WAITING:
"Finalizer" daemon prio=10 tid=0x43e1e000 nid=0x3ff in Object.wait() [0x43dfe000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x4fe053e8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:133)
        - locked <0x4fe053e8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:149)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)
编辑:
然后我们尝试添加-XX:+UseConcMarkSweepGC,但没有成功,只有OutOfMemoryErrors 的频率减少,所以我们首先认为它有所帮助.
最后,我们怀疑JVM错误并从OpenJDK 1.6.0_30升级到Oracle JDK 1.7.0_51,问题消失了(至少看起来如此,在过去4小时内使用的堆没有增长).我们不记得finalize方法有任何变化,我们也没有升级任何库,在那段时间里只有很小的发展.问题不会在我们的测试服务器上重现,具有相同的配置,除了它是64位JVM而生产服务器是32位.
问题是:什么可能是对象没有最终确定和Finalizer线程等待下一个对象的原因?我们是否正确分析了堆转储?
谢谢你的所有答案.
主要问题在于主题,但是让我展示我对Java中最终化过程的看法,以便我可以向您提出更多要求.
那么gc通过标记所有活动对象来启动垃圾收集.当所有可到达的对象都标记为"实时"时.所有其他对象都无法访问.下一步是检查每个无法到达的对象,并确定它是否可以立即进行清理,或者应该首先完成.如果对象的finalize方法有一个主体,那么gc会想到下一个方法,那么这个对象是可以最终确定的并且应该最终确定; 如果对象的finalize方法有一个空体(protected void finalize(){})那么它不能最终化并且现在可以被gc清理.(我是对的吗?)
所有可终结的对象将放在同一个队列中,以便稍后逐一完成.据我所知,可终结的对象可以花费大量时间放在队列中,同时等待轮到他们完成.这可能发生,因为通常只有一个名为Finalizer的线程从队列中获取对象并调用它们的finalize方法,当我们在某个对象的finalize方法中有一些耗时的操作时,队列中的其他对象将等待很长时间才能完成.好的,当一个对象完成后,它被标记为FINALIZED并从队列中删除.在下一个垃圾收集过程中,收集器将看到此对象无法访问(再次)并且具有非空的finalize方法(再次),因此该对象应该被放入队列中(再次) - 但它不会因为收集器以某种方式看到这个对象被标记为FINALIZED.(这是我的主要问题:这个对象被标记为FINALIZED的方式,收集器如何知道该对象不应该再次终结?)
我读过的关于java finalize方法的所有内容都说不要使用它.它似乎几乎永远不会被保证被调用,即使它存在也可能出现问题.
还有一些其他问题要求何时使用它,似乎普遍的共识绝不是.
我自己从未使用它(主要是因为没有警告),我没有看到它在任何地方使用过.
是否有任何合适的案例?有没有其他选择的情况?
如果没有,为什么会在那里?是否有内部类使用它并要求方法可用?或者它只是不应该存在的东西?
我对使用它的时间并不是那么感兴趣(已经用"从不"回答),但澄清为什么它甚至给出了"从不"的回答.如果它是如此无用和危险,为什么它没有被折旧和删除?
适用于PhantomReference的 Javadoc 8 指出:
与Java终结机制相比,虚拟引用最常用于以更灵活的方式调度事前清理操作。
因此,我尝试创建一个线程,该线程正在调用close()符合垃圾回收条件的Test Object方法。在run()试图获得所有测试对象的预验。
实际上,检索到的测试对象都是null。预期的行为是检索测试对象并调用该close方法。
无论创建多少个测试对象,都不会在验尸前捕获单个测试对象(您必须增加超时并多次调用GC)。
我究竟做错了什么?这是Java错误吗?
我试图创建一个最小,完整和可验证的示例,但是它仍然很长。我java version "1.8.0_121"在Windows 7 64位上使用32位。
public class TestPhantomReference {
    public static void main(String[] args) throws InterruptedException {
        // Create AutoClose Thread and start it
        AutoCloseThread thread = new AutoCloseThread();
        thread.start();
        // Add 10 Test Objects to the AutoClose Thread
        // Test Objects are directly eligible for GC
        for (int i …java garbage-collection phantom-reference finalize finalizer