如何防止对象被垃圾收集?

Rah*_*arg 32 java garbage-collection

如何防止对象被垃圾收集?

是否有最终确定或幻像参考或任何其他方法的方法?

我在接受采访时被问到这个问题.面试官建议finalize()可以使用.

Tob*_*ias 32

保持参考.如果您的对象过早收集,则表明您的应用程序设计存在错误.

垃圾收集器仅收集应用程序中没有引用的对象.如果没有自然引用收集对象的对象,请问自己为什么要保持活着.

您通常没有引用但想要保留对象的一个​​用例是单例.在这种情况下,您可以使用静态变量.单例的一种可能实现如下所示:

public class Singleton {
  private static Singleton uniqueInstance;

  private Singleton() {
    }

  public static synchronized Singleton getInstance() {
    if (uniqueInstance == null) {
      uniqueInstance = new Singleton();
    }
    return uniqInstance;
  }
}
Run Code Online (Sandbox Code Playgroud)

编辑:从技术上讲,您可以在终结器中的某处存储引用.这将阻止收集对象,直到收集器再次确定没有更多引用.但是,终结器最多只会调用一次,因此必须确保在第一次收集后不需要完成对象(包括其超类).但是,我建议你不要在实际程序中使用这种技术.(这会让像我这样的同事大喊大叫WTF!?;)

  protected void finalize() throws Throwable {
    MyObjectStore.getInstance().store(this);
    super.finalize(); // questionable, but you should ensure calling it somewhere.
  }
Run Code Online (Sandbox Code Playgroud)

  • 您可以激发其他对象来存储对象的新引用.但是请注意,对于任何给定对象,最终调用最多一次,因此当收集器确定可以再次收集它时,它不会再次完成. (2认同)

CPe*_*ins 9

你的面试官正在寻找的技巧答案可能是他希望你知道你可以通过强制内存泄漏来防止垃圾收集移除对象.

显然,如果你在一些长期存在的环境中保留对象的引用,它将不会被收集,但这不是OP的招聘人员所询问的.这不是在finalize方法中发生的事情.

你可以做什么来防止在finalize方法中收集垃圾是一个无限循环,你可以在其中调用Thread.yield();(可能是为了防止空循环被优化掉):

@Override
protected void finalize() throws Throwable { 
    while (true) { 
        Thread.yield(); 
    } 
} 
Run Code Online (Sandbox Code Playgroud)

我在这里引用的是Elliot Back的一篇文章,其中描述了通过这种方法强制内存泄漏.

只是另一种方法,其中finalize方法是邪恶的.


Cir*_*四事件 6

最好的方法是使用Unsafe,尽管ByteBuffer在某些情况下可能是一种解决方法。

还要搜索关键字“堆外”内存。

不安全

优势ByteBuffer

  • 允许直接表示对象,而无需序列化,因此速度更快
  • 无边界检查,因此更快
  • 显式解除分配控制
  • 可以分配更多的JVM限制

但是,开始工作并不容易。以下文章中介绍了该方法:

它们全部包括以下步骤:

  • 我们需要sizeof不安全的操作员。有人问如何制作:在Java中,确定对象大小的最佳方法是什么?。最好的选择可能是instrumentAPI,但这需要您创建一个Jar并使用特殊的命令行选项...

  • 一旦有了sizeof,用分配足够的内存Unsafe#allocateMemory,基本上是a malloc并返回一个地址

  • 创建一个常规的堆对象,并使用将其复制到分配的内存中Unsafe#copyMemory。为此,您需要堆上对象的地址和对象的大小

  • 设置一个Object指向已分配内存的指针,然后将其强制转换Object为您的类。

    似乎不可能直接使用Unsafe设置变量的地址,因此我们需要将对象包装到数组或包装器对象中,然后使用Unsafe#arrayBaseOffsetUnsafe#objectFieldOffset

  • 完成后,使用以下命令释放分配的内存 freeMemory

如果我不这样做会导致段错误,我将发布一个示例:-)

字节缓冲区

相对于不安全的优势:

  • 在Java版本中稳定,而Unsafe可能会崩溃
  • 进行边界检查,比...更安全...不安全,这会导致内存泄漏和SIGSEGV

JLS说

直接缓冲区的内容可能驻留在普通垃圾回收堆的外部。

原语用法示例:

ByteBuffer bb = ByteBuffer.allocateDirect(8);

bb.putInt(0, 1);
bb.putInt(4, 2);
assert bb.getInt(0) == 1;
assert bb.getInt(4) == 2;

// Bound chekcs are done.
boolean fail = false;
try {
    bb.getInt(8);
} catch(IndexOutOfBoundsException e) {
    fail = true;
}
assert fail;
Run Code Online (Sandbox Code Playgroud)

相关主题: