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

时间:2021-11-08 03:04:57

How to prevent an object from getting garbage collected?

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

Are there any approaches by finalize or phantom reference or any other approaches?

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

I was asked this question in an interview. The interviewer suggested that finalize() can be used.

我在接受采访时被问到这个问题。采访者建议使用finalize()。

9 个解决方案

#1


32  

Hold a reference. If your object is getting collected prematurely, it is a symptom that you have a bug in the design of your application.

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

The garbage collector collects only objects to which there is no reference in your application. If there is no object that would naturally reference the collected object, ask yourself why it should be kept alive.

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

One usecase in which you typically have no references, but want to keep an object is a singleton. In this case, you could use a static variable. One possible implementation of a singleton would look like this:

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

public class Singleton {
  private static Singleton uniqueInstance;

  private Singleton() {
    }

  public static synchronized Singleton getInstance() {
    if (uniqueInstance == null) {
      uniqueInstance = new Singleton();
    }
    return uniqInstance;
  }
}

Edit: Technically, you can store a reference somewhere in your finalizer. This will prevent the object from being collected until the collector determines again that there are no more references. The finalizer will only be called at most once, however, so you must ensure that your object (including its superclasses) need not be finalized after the first collection. I would advise you, however, not to use this technique in actual programs. (It will leave colleagues like me yelling WTF!? ;)

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

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

#2


9  

The trick answer your interviewer was looking for is probably that he wants you to know that you can prevent garbage collection from removing an object by forcing a memory leak.

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

Obviously, if you keep a reference to the object in some long-lived context, it won't be collected, but that's not what the OP's recruiter asked about. That's not something which happens in the finalize method.

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

What you can do to prevent garbage collection from within the finalize method is to write an infinite loop, in which you call Thread.yield();(presumably to keep an empty loop from being optimized away):

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

@Override
protected void finalize() throws Throwable { 
    while (true) { 
        Thread.yield(); 
    } 
} 

My reference here is an article by Elliot Back, in which describes forcing a memory leak by this method.

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

Just another way in which finalize methods are evil.

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

#3


4  

The best way is to use Unsafe, although ByteBuffer might be a possible workaround for some cases.

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

Also search for the keyword "off-heap" memory.

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

Unsafe

Advantages over ByteBuffer:

优于ByteBuffer:

  • allows objects to be represented directly, without for serialization and thus faster
  • 允许直接表示对象,而不需要序列化,因此更快

  • no bounds checking, so faster
  • 没有边界检查,所以更快

  • explicit deallocation control
  • 显式释放控制

  • can allocate more than the JVM limit
  • 可以分配超过JVM限制

It is not however easy to get working. The method is described in the following articles:

然而,工作并不容易。该方法在以下文章中描述:

They all consist of the following steps:

它们都包括以下步骤:

  • we need a sizeof operator, which Unsafe does not have. How to make one was asked at: In Java, what is the best way to determine the size of an object?. The best options is likely the instrument API, but that requires you to create a Jar and use special command line options...

    我们需要一个sizeof运算符,Unsafe没有。如何制作一个被问到:在Java中,确定对象大小的最佳方法是什么?最好的选择可能是仪器API,但这要求您创建一个Jar并使用特殊的命令行选项......

  • once we have sizeof, allocate enough memory with Unsafe#allocateMemory, which is basically a malloc and returns an address

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

  • create a regular on heap object, copy it to the allocated memory with Unsafe#copyMemory. To do this, you need to the address of the on-heap object, and the size of the object

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

  • set an Object to point to the allocated memory, then cast the Object to your class.

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

    It does not seem possible to set the address of a variable directly with Unsafe, so we need to wrap the object into an array or wrapper object, and use Unsafe#arrayBaseOffset or Unsafe#objectFieldOffset.

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

  • once you are done, free the allocated memory with freeMemory

    完成后,使用freeMemory释放已分配的内存

If I ever get this to not segfault I will post an example :-)

如果我得到这个不是段错误,我会发一个例子:-)

ByteBuffer

Advantages over Unsafe:

优于不安全的优点:

  • stable across Java versions while Unsafe may break
  • 在Java版本中稳定,而不安全可能会破坏

  • does bound checking, so safer than... Unsafe, which allows for memory leaks and SIGSEGV
  • 绑定检查,比...更安全,不安全,允许内存泄漏和SIGSEGV

JLS says:

The contents of direct buffers may reside outside of the normal garbage-collected heap.

直接缓冲区的内容可能位于正常的垃圾收集堆之外。

Example of usage with primitives:

使用原语的示例:

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;

Related threads:

#4


3  

If there is still a reference to the object, it won't get garbage collected. If there aren't any references to it, you shouldn't care.

如果仍然存在对该对象的引用,则不会收集垃圾。如果没有任何参考,你不应该在乎。

In other words - the garbage collector only collects garbage. Let it do its job.

换句话说 - 垃圾收集器只收集垃圾。让它发挥作用。

#5


2  

I suspect what you might be referring to is if your finalize method stashes away a reference to the object being finalized. In this case (if my reading of the Java Language Spec is correct) the finalize method will never be re-run, but the object will not yet be garbage collected.

我怀疑你可能指的是你的finalize方法是否存在对正在最终确定的对象的引用。在这种情况下(如果我读取Java语言规范是正确的),finalize方法将永远不会重新运行,但该对象将不会被垃圾回收。

This is not the sort of thing one does in real life, except possibly by accident!

这不是人们在现实生活中所做的事情,除非偶然!

#6


2  

This sounds like one of those interview-only-time-you'll-see-it questions. finalize() is run when your object is getting garbage collected, so it'd be pretty perverse to put something in there to prevent collection. Normally you just hold a reference and that's all you need.

这听起来像是一次只能面试的时间 - 你会看到它的问题。当你的对象被垃圾收集时,会运行finalize(),因此在那里放置一些东西以阻止收集是非常不正常的。通常你只需要一个参考,这就是你所需要的。

I'm not even sure what would happen if you'd create a new reference for something in the finalizer - since the garbage collector's already decided to collect it would you then end up with a null ref? Seems like a poor idea, in any case. e.g.

我甚至不确定如果你在终结器中为某些东西创建一个新的引用会发生什么 - 因为垃圾收集器已经决定收集它然后你最终得到一个空引用?在任何情况下,似乎都是一个糟糕的主意。例如

public class Foo {
   static Foo reference;
  ...
  finalize (){ 
     reference = this; 
  }
}

I doubt this would work, or it might work but be dependant on the GC implenetation, or be "unspecified behavior". Looks evil, though.

我怀疑这会起作用,或者它可能有效但依赖于GC实现,或者是“未指定的行为”。但看起来很邪恶。

#7


1  

The key point is if we set the real reference variable pointing to the object null,although we have instance variables of that class pointing to that object not set to null. The object is automatically eligible for garbage collection.if save the object to GC, use this code...

关键点是,如果我们设置指向对象null的实际引用变量,尽管我们有该类的实例变量指向该对象未设置为null。该对象自动符合垃圾回收的条件。如果将对象保存到GC,请使用此代码...

public class GcTest {

    public int id;
    public String name;
    private static GcTest gcTest=null;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();

        System.out.println("In finalize method.");
        System.out.println("In finalize :ID :"+this.id);
        System.out.println("In finalize :ID :"+this.name);

        gcTest=this;

    }

    public static void main(String[] args) {

        GcTest myGcTest=new GcTest();
        myGcTest.id=1001;
        myGcTest.name="Praveen";
        myGcTest=null;

        // requesting Garbage Collector to execute.
        // internally GC uses Mark and Sweep algorithm to clear heap memory.
        // gc() is a native method in RunTime class.

        System.gc();   // or Runtime.getRuntime().gc();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("\n------- After called GC () ---------\n");
        System.out.println("Id :"+gcTest.id);
        System.out.println("Name :"+gcTest.name);


    }

}

Output :

In finalize method.
In finalize :ID :1001
In finalize :ID :Praveen

在finalize方法中。最终确定:ID:1001最终确定:ID:Praveen

------- After called GC () --------

-------调用GC()之后--------

Id :1001
Name :Praveen

Id:1001姓名:Praveen

#8


1  

I wonder if what they're going for is the pattern with resource pools (e.g. for network/db connections, or threads) where you use finalize to return a resource to the pool so that the actual object holding the resource isn't GC'ed.

我想知道他们想要的是资源池的模式(例如,对于网络/数据库连接或线程),您使用finalize将资源返回到池中,以便保存资源的实际对象不是GC'编辑。

Stupid example, in Java-like pseudocode and missing any kind of synchronization:

愚蠢的例子,在类似Java的伪代码中并且缺少任何类型的同步:

class SlowResourceInternal {
   private final SlowResourcePool parent;
   <some instance data>

   returnToPool() {
       parent.add(this);
   }
}

class SlowResourceHolder {
    private final SlowResourceInternal impl;

    <delegate actual stuff to the internal object>

    finalize() {
        if (impl != null) impl.returnToPool();
    }
}

#9


0  

I believe there is a pattern out there for this. Not sure if it the factory pattern. But you have one object that creates all your objects and holds a reference to them. When you are finished with them, you de-reference them in the factory, making the call explicit.

我相信这有一种模式。不确定它是否是工厂模式。但是你有一个对象可以创建所有对象并保存对它们的引用。完成它们后,在工厂中取消它们的引用,使调用显式化。

#1


32  

Hold a reference. If your object is getting collected prematurely, it is a symptom that you have a bug in the design of your application.

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

The garbage collector collects only objects to which there is no reference in your application. If there is no object that would naturally reference the collected object, ask yourself why it should be kept alive.

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

One usecase in which you typically have no references, but want to keep an object is a singleton. In this case, you could use a static variable. One possible implementation of a singleton would look like this:

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

public class Singleton {
  private static Singleton uniqueInstance;

  private Singleton() {
    }

  public static synchronized Singleton getInstance() {
    if (uniqueInstance == null) {
      uniqueInstance = new Singleton();
    }
    return uniqInstance;
  }
}

Edit: Technically, you can store a reference somewhere in your finalizer. This will prevent the object from being collected until the collector determines again that there are no more references. The finalizer will only be called at most once, however, so you must ensure that your object (including its superclasses) need not be finalized after the first collection. I would advise you, however, not to use this technique in actual programs. (It will leave colleagues like me yelling WTF!? ;)

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

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

#2


9  

The trick answer your interviewer was looking for is probably that he wants you to know that you can prevent garbage collection from removing an object by forcing a memory leak.

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

Obviously, if you keep a reference to the object in some long-lived context, it won't be collected, but that's not what the OP's recruiter asked about. That's not something which happens in the finalize method.

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

What you can do to prevent garbage collection from within the finalize method is to write an infinite loop, in which you call Thread.yield();(presumably to keep an empty loop from being optimized away):

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

@Override
protected void finalize() throws Throwable { 
    while (true) { 
        Thread.yield(); 
    } 
} 

My reference here is an article by Elliot Back, in which describes forcing a memory leak by this method.

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

Just another way in which finalize methods are evil.

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

#3


4  

The best way is to use Unsafe, although ByteBuffer might be a possible workaround for some cases.

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

Also search for the keyword "off-heap" memory.

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

Unsafe

Advantages over ByteBuffer:

优于ByteBuffer:

  • allows objects to be represented directly, without for serialization and thus faster
  • 允许直接表示对象,而不需要序列化,因此更快

  • no bounds checking, so faster
  • 没有边界检查,所以更快

  • explicit deallocation control
  • 显式释放控制

  • can allocate more than the JVM limit
  • 可以分配超过JVM限制

It is not however easy to get working. The method is described in the following articles:

然而,工作并不容易。该方法在以下文章中描述:

They all consist of the following steps:

它们都包括以下步骤:

  • we need a sizeof operator, which Unsafe does not have. How to make one was asked at: In Java, what is the best way to determine the size of an object?. The best options is likely the instrument API, but that requires you to create a Jar and use special command line options...

    我们需要一个sizeof运算符,Unsafe没有。如何制作一个被问到:在Java中,确定对象大小的最佳方法是什么?最好的选择可能是仪器API,但这要求您创建一个Jar并使用特殊的命令行选项......

  • once we have sizeof, allocate enough memory with Unsafe#allocateMemory, which is basically a malloc and returns an address

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

  • create a regular on heap object, copy it to the allocated memory with Unsafe#copyMemory. To do this, you need to the address of the on-heap object, and the size of the object

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

  • set an Object to point to the allocated memory, then cast the Object to your class.

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

    It does not seem possible to set the address of a variable directly with Unsafe, so we need to wrap the object into an array or wrapper object, and use Unsafe#arrayBaseOffset or Unsafe#objectFieldOffset.

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

  • once you are done, free the allocated memory with freeMemory

    完成后,使用freeMemory释放已分配的内存

If I ever get this to not segfault I will post an example :-)

如果我得到这个不是段错误,我会发一个例子:-)

ByteBuffer

Advantages over Unsafe:

优于不安全的优点:

  • stable across Java versions while Unsafe may break
  • 在Java版本中稳定,而不安全可能会破坏

  • does bound checking, so safer than... Unsafe, which allows for memory leaks and SIGSEGV
  • 绑定检查,比...更安全,不安全,允许内存泄漏和SIGSEGV

JLS says:

The contents of direct buffers may reside outside of the normal garbage-collected heap.

直接缓冲区的内容可能位于正常的垃圾收集堆之外。

Example of usage with primitives:

使用原语的示例:

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;

Related threads:

#4


3  

If there is still a reference to the object, it won't get garbage collected. If there aren't any references to it, you shouldn't care.

如果仍然存在对该对象的引用,则不会收集垃圾。如果没有任何参考,你不应该在乎。

In other words - the garbage collector only collects garbage. Let it do its job.

换句话说 - 垃圾收集器只收集垃圾。让它发挥作用。

#5


2  

I suspect what you might be referring to is if your finalize method stashes away a reference to the object being finalized. In this case (if my reading of the Java Language Spec is correct) the finalize method will never be re-run, but the object will not yet be garbage collected.

我怀疑你可能指的是你的finalize方法是否存在对正在最终确定的对象的引用。在这种情况下(如果我读取Java语言规范是正确的),finalize方法将永远不会重新运行,但该对象将不会被垃圾回收。

This is not the sort of thing one does in real life, except possibly by accident!

这不是人们在现实生活中所做的事情,除非偶然!

#6


2  

This sounds like one of those interview-only-time-you'll-see-it questions. finalize() is run when your object is getting garbage collected, so it'd be pretty perverse to put something in there to prevent collection. Normally you just hold a reference and that's all you need.

这听起来像是一次只能面试的时间 - 你会看到它的问题。当你的对象被垃圾收集时,会运行finalize(),因此在那里放置一些东西以阻止收集是非常不正常的。通常你只需要一个参考,这就是你所需要的。

I'm not even sure what would happen if you'd create a new reference for something in the finalizer - since the garbage collector's already decided to collect it would you then end up with a null ref? Seems like a poor idea, in any case. e.g.

我甚至不确定如果你在终结器中为某些东西创建一个新的引用会发生什么 - 因为垃圾收集器已经决定收集它然后你最终得到一个空引用?在任何情况下,似乎都是一个糟糕的主意。例如

public class Foo {
   static Foo reference;
  ...
  finalize (){ 
     reference = this; 
  }
}

I doubt this would work, or it might work but be dependant on the GC implenetation, or be "unspecified behavior". Looks evil, though.

我怀疑这会起作用,或者它可能有效但依赖于GC实现,或者是“未指定的行为”。但看起来很邪恶。

#7


1  

The key point is if we set the real reference variable pointing to the object null,although we have instance variables of that class pointing to that object not set to null. The object is automatically eligible for garbage collection.if save the object to GC, use this code...

关键点是,如果我们设置指向对象null的实际引用变量,尽管我们有该类的实例变量指向该对象未设置为null。该对象自动符合垃圾回收的条件。如果将对象保存到GC,请使用此代码...

public class GcTest {

    public int id;
    public String name;
    private static GcTest gcTest=null;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();

        System.out.println("In finalize method.");
        System.out.println("In finalize :ID :"+this.id);
        System.out.println("In finalize :ID :"+this.name);

        gcTest=this;

    }

    public static void main(String[] args) {

        GcTest myGcTest=new GcTest();
        myGcTest.id=1001;
        myGcTest.name="Praveen";
        myGcTest=null;

        // requesting Garbage Collector to execute.
        // internally GC uses Mark and Sweep algorithm to clear heap memory.
        // gc() is a native method in RunTime class.

        System.gc();   // or Runtime.getRuntime().gc();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("\n------- After called GC () ---------\n");
        System.out.println("Id :"+gcTest.id);
        System.out.println("Name :"+gcTest.name);


    }

}

Output :

In finalize method.
In finalize :ID :1001
In finalize :ID :Praveen

在finalize方法中。最终确定:ID:1001最终确定:ID:Praveen

------- After called GC () --------

-------调用GC()之后--------

Id :1001
Name :Praveen

Id:1001姓名:Praveen

#8


1  

I wonder if what they're going for is the pattern with resource pools (e.g. for network/db connections, or threads) where you use finalize to return a resource to the pool so that the actual object holding the resource isn't GC'ed.

我想知道他们想要的是资源池的模式(例如,对于网络/数据库连接或线程),您使用finalize将资源返回到池中,以便保存资源的实际对象不是GC'编辑。

Stupid example, in Java-like pseudocode and missing any kind of synchronization:

愚蠢的例子,在类似Java的伪代码中并且缺少任何类型的同步:

class SlowResourceInternal {
   private final SlowResourcePool parent;
   <some instance data>

   returnToPool() {
       parent.add(this);
   }
}

class SlowResourceHolder {
    private final SlowResourceInternal impl;

    <delegate actual stuff to the internal object>

    finalize() {
        if (impl != null) impl.returnToPool();
    }
}

#9


0  

I believe there is a pattern out there for this. Not sure if it the factory pattern. But you have one object that creates all your objects and holds a reference to them. When you are finished with them, you de-reference them in the factory, making the call explicit.

我相信这有一种模式。不确定它是否是工厂模式。但是你有一个对象可以创建所有对象并保存对它们的引用。完成它们后,在工厂中取消它们的引用,使调用显式化。