可最终确定对象的前期成本是多少?

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)

但是,如果可最终化的行为意味着必须保留指向该对象的附加指针,那么对于该应用程序而言,可最终确定的前期成本将被认为是高的.如果它意味着必须分配额外的对象,那几乎肯定会过高......因此,我的问题是:最终化的直接前期成本是多少?

apa*_*gin 8

终结器非常糟糕, 不仅因为保留问题,而且还来自性能方面.

在Oracle JDK/OpenJDK中,带有finalize方法的对象由Finalizer的实例支持,Finalizer是其子类java.lang.ref.Reference.

所有终结器都在对象的构造函数的末尾注册,分两步:从Java到VM的调用,然后调用Finalizer.register().JIT编译器无法内联这种双转换Java-> VM-> Java.但最糟糕的是,Finalizer的构造函数在全局锁定下创建了一个链表!(捂脸)

终结器在内存占用方面也很糟糕:除了所有参考字段外,它们还有两个额外的字段:nextprev.

PhantomReferences比终结器要好得多:

  • 它们的构造不需要转换到VM和后面,并且可以内联;
  • 除了继承之外,他们没有额外的字段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)