在Java中讨论可finalizable对象时,通常会讨论当可finalizable对象(及其相关资源)不能被快速垃圾收集时所发生的常见间接成本。
目前,我更感兴趣的是可finalizable的实际直接成本是多少,包括内存成本和对象分配时间成本。我在很多地方看到过间接的引用,例如Oracle's article on finalization memory retention issues注解:
当obj
被分配时,JVM在内部记录obj
是可finalizable的。这通常会减慢现代JVM的快速分配路径。
JVM如何记录一个对象示例是可finalizable的,这样做的内存和性能成本是多少?
对于那些对我的具体应用感兴趣的人:
我们生产并保留了数百万个难以置信的轻质物体;向这些对象添加一个指针的代价非常高,因此我们做了大量工作来删除指针,而不是使用压缩到字段位子集中的较小数字id。解压缩数字允许从使用Map存储它们的Pool中检索具有该id的共享不可变属性。
剩下的问题是如何处理不再使用的属性值的垃圾回收。
已经考虑的一种策略是使用引用计数;当对象被创建并检索值的汇集ID时,该值的引用计数递增;当不再使用它时,它必须递减。
确保发生这种递减的一个选项是添加以下finalize方法:
public void finalize() {
Pool.release(getPropertyId());
}
然而,如果finalizable的行为本身就意味着必须保留指向对象的额外指针,那么finalizable的前期成本对于这个应用程序来说就很高了。如果它意味着必须分配额外的对象,那么几乎可以肯定成本太高了......因此,我的问题是:可定案的直接前期成本是多少?
1条答案
按热度按时间zpf6vheq1#
终结器糟糕不仅是因为保留问题,而且从性能Angular 来看也是如此。
在Oracle JDK / OpenJDK中,具有
finalize
方法的对象由Finalizer(java.lang.ref.Reference
的子类)的示例支持。所有终结器都在对象构造函数的末尾注册,分两步进行:一个从Java到VM的调用,然后调用Finalizer.register()。JIT编译器不能内联Java-〉VM-〉Java的这种双重转换。但最糟糕的是Finalizer的构造函数在全局锁下生成了一个链表!(facepalm)
终结器在内存占用方面也不好:除了所有参考字段外,它们还有两个额外字段:
next
和prev
中的一个或多个。PhantomReferences比终结器好得多:
java.lang.ref.Reference
继承之外没有额外的字段;This benchmark比较可finalizable对象和PhantomReference支持的对象的分配速度: