The*_*ock 9 java jvm finalization
Java中可终结对象的讨论通常讨论当最终化对象(及其相关资源)无法快速被垃圾收集时发生的常见间接成本.
目前,我更感兴趣的是,无论是在内存方面还是在对象分配时间内,可实现的最终直接成本是什么.我已经看到在很多地方倾向于提到这种成本的存在,例如,Oracle关于最终化内存保留问题的文章指出:
当
obj被分配时,JVM内部记录中obj是终结.这通常会减慢现代JVM具有的快速分配路径.
JVM如何记录对象实例的最终结果,以及这样做的内存和性能成本是多少?
对于对我的具体应用感兴趣的人:
我们生产和保留了数百万个极其轻巧的物体; 添加一个指向这些对象的指针是非常昂贵的,所以我们已经做了相当多的工作来删除它们的指针,而是使用较小的数字ID打包到字段的一部分位.解包该数字允许从使用Map存储它们的池中检索具有该id的共享不可变属性.
剩下的问题是如何处理不再使用的属性值的垃圾收集.
已经考虑的一种策略是使用引用计数; 当创建对象并检索值的池化id时,该值的引用计数递增; 当它不再使用时,必须递减.
确保此减量发生的一个选项是添加以下finalize方法:
public void finalize() {
Pool.release(getPropertyId());
}
Run Code Online (Sandbox Code Playgroud)
但是,如果可最终化的行为意味着必须保留指向该对象的附加指针,那么对于该应用程序而言,可最终确定的前期成本将被认为是高的.如果它意味着必须分配额外的对象,那几乎肯定会过高......因此,我的问题是:最终化的直接前期成本是多少?
终结器非常糟糕, 不仅因为保留问题,而且还来自性能方面.
在Oracle JDK/OpenJDK中,带有finalize方法的对象由Finalizer的实例支持,Finalizer是其子类java.lang.ref.Reference.
所有终结器都在对象的构造函数的末尾注册,分两步:从Java到VM的调用,然后调用Finalizer.register().JIT编译器无法内联这种双转换Java-> VM-> Java.但最糟糕的是,Finalizer的构造函数在全局锁定下创建了一个链表!(捂脸)
终结器在内存占用方面也很糟糕:除了所有参考字段外,它们还有两个额外的字段:next和prev.
PhantomReferences比终结器要好得多:
java.lang.ref.Reference;此基准测试比较可终结对象的分配速度和PhantomReference支持的对象:
Benchmark Mode Cnt Score Error Units
Finalizer.finalizable thrpt 5 2171,312 ± 1469,705 ops/ms
Finalizer.phantom thrpt 5 61280,612 ± 692,922 ops/ms
Finalizer.plain thrpt 5 225752,307 ± 7618,304 ops/ms
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
219 次 |
| 最近记录: |