如何防止对象被垃圾回收?
是否有使用finalize或虚引用或其他方法的途径?
我在面试中被问到这个问题。面试官建议可以使用finalize()
方法。
如何防止对象被垃圾回收?
是否有使用finalize或虚引用或其他方法的途径?
我在面试中被问到这个问题。面试官建议可以使用finalize()
方法。
保持引用。如果您的对象过早地被回收,那么这是您的应用程序设计存在错误的表现。
垃圾收集器仅收集应用程序中没有引用的对象。如果没有任何对象自然引用被收集的对象,请思考为什么它应该被保持存活。
通常情况下您需要保持一个没有引用的对象是单例模式。在这种情况下,您可以使用静态变量。一个可能的单例模式实现如下:
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqInstance;
}
}
编辑:从技术上讲,您可以在终结器中存储一个引用。这将防止对象被收集,直到收集器再次确定没有更多的引用。然而,终结器最多只会被调用一次,因此您必须确保您的对象(包括其超类)在第一次回收后不需要终结。不过,我建议您在实际程序中不要使用这种技术。(这会让像我这样的同事大喊WTF!?;)
protected void finalize() throws Throwable {
MyObjectStore.getInstance().store(this);
super.finalize(); // questionable, but you should ensure calling it somewhere.
}
@Override
protected void finalize() throws Throwable {
while (true) {
Thread.yield();
}
}
我参考的文章是Elliot Back写的,其中描述了通过这种方法来强制产生内存泄漏。
这只是表明了finalize方法是有害的的另一种方式。
ByteBuffer
可能是某些情况下的一种可行解决方案。ByteBuffer
相比的优势:
它们都包括以下步骤:
我们需要一个sizeof
运算符,而Unsafe没有。如何创建一个被问到了:在Java中,确定对象大小的最佳方法是什么?。最好的选择可能是instrument
API,但这需要您创建一个Jar并使用特殊的命令行选项...
一旦我们有了sizeof
,就可以使用Unsafe#allocateMemory
分配足够的内存,它基本上是一个malloc
并返回一个地址
创建一个常规的堆对象,使用Unsafe#copyMemory
将其复制到已分配的内存中。为此,您需要知道堆对象的地址和对象的大小
将一个Object
指向已分配的内存,然后将Object
强制转换为您的类。
似乎不可能直接使用Unsafe设置变量的地址,因此我们需要将对象包装成数组或包装器对象,并使用Unsafe#arrayBaseOffset
或Unsafe#objectFieldOffset
。
完成后,使用freeMemory
释放已分配的内存
如果我能让它不再发生段错误,我会发布一个示例 :-)
ByteBuffer
相比Unsafe的优势:
直接缓冲区的内容可能驻留在正常垃圾回收堆之外。
使用基本类型的示例:
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;
相关主题:
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);
}
}
输出:
在finalize方法中。
在finalize中:ID:1001
在finalize中:ID:Praveen
------- 在调用GC()之后 --------
ID:1001
姓名:Praveen
public class Foo {
static Foo reference;
...
finalize (){
reference = this;
}
}
我怀疑这个方法是否有效,或者它可能依赖于GC实现,或者是"未指定的行为"。尽管看起来很邪恶。
finalize
方法是否将一个指向正在被终结的对象的引用藏起来。如果是这样(如果我对Java语言规范的理解正确),那么finalize
方法永远不会再次运行,但对象还没有被垃圾回收。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();
}
}
我相信这里有一个相关的模式。不确定它是否为工厂模式。但是,你只需要一个对象来创建所有的对象并保存对它们的引用。当你完成使用它们后,在工厂中将其解除引用,让调用变得明确。