什么是对象复活?
当没有其他对象引用该对象时,该Java对象可以进行垃圾回收。 当JVM:s垃圾收集器最终将要删除未使用的对象时,将调用该对象的finalize()
方法。 但是,如果我们再次使用对象自己的finalize()
方法重新创建对该对象的引用,则该对象可以复活。 在这种情况下,JVM将检测到再次引用了该对象,并避免将其删除。 隐喻地,该物体已经从死亡中复活了……
public class Immortal {
private static final Set<Immortal> immortals = new HashSet<>();
@Override
protected void finalize() throws Throwable {
System.out.println(Immortal.class.getSimpleName() + "::finalize for " + this);
immortals.add(this); // Resurrect the object by creating a new reference
}
}
可以通过以下方式测试复活属性:
public class NewMain {
public static void main(String[] args) {
new Immortal();
System.gc();
sleep(1_000);
System.gc();
prompt("Press any key...");
}
private static void prompt(String msg) {
try {
System.out.println(msg);
System.in.read();
} catch (IOException io) {
}
}
private static void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException ie) {
}
}
}
这将给出以下输出:
Immortal::finalize for com.blogspot.minborgsjavapot.resurected_object.Immortal@635cb856
Press any key...
通过检查Java堆,我们还可以看到尽管对象的终结器被调用,该对象仍然存在:
pemi$ jps
21735 NewMain
21736 Jps
pemi$ jmap -histo 21735 | grep Immortal
164: 1 16 com.blogspot.minborgsjavapot.resurected_object.Immortal
终结器被调用了多少次?
如果以后取消引用了复活的对象,则该对象又有资格进行垃圾回收。 但是,这次
由于Java最多只能调用finalizer一次,因此不会再次调用finalize()
方法。 我们可能还记得,不能保证终结器会被调用。 例如,如果程序由于任何原因终止,则仅放弃JVM中的对象,并且最终定形器将根本不会被调用,如在本示例中可以看到的:
public class NewMain2 {
public static void main(String[] args) {
new Immortal();
}
}
当我们运行上述代码片段时,我们观察到从未调用Immortal::finalizer
。
对象复活好吗?
与使用finalize()
方法一样,我们必须非常谨慎。 对我们Java开发人员的一般建议是根本不使用finalize()
。 此外,有人可能会说,复活一个对象与故意造成内存泄漏是一样的。
但是,有一些有趣的对象复活应用程序。 也许我们想对我们的对象进行事后分析,而又不更改使用该对象的实际应用程序。 通过使用对象复活,我们可以保存那些对象并在以后分析它们的内部状态,而与使用它们的应用程序无关。
翻译自: https://www.javacodegeeks.com/2016/03/java-immortal-objects-object-resurrection.html