2015-05-08 42 views
9

关于Java中可终结对象的讨论通常讨论当可终结对象(及其关联资源)无法快速垃圾收集时发生的常见间接成本。一个对象可以终结的前期成本是多少?

我现在更感兴趣的是,在内存条件和对象分配时间中,实际可终止的直接成本是多少。我已经看到了在一些地方这种成本的存在,拐弯抹角例如,Oracle's article on finalization memory retention issues注:

obj分配,在JVM内部记录了obj是终结。这通常会减慢现代JVM具有的快速分配路径。

JVM如何记录一个对象实例是否可终止,以及这样做的内存和性能成本如何?

对于那些有兴趣在我的具体应用:

我们生产和保留数百万难以置信的轻量级的对象;向这些对象添加单个指针的代价非常高昂,所以我们已经做了一些工作来删除指针,而不是使用打包到字段子集中的较小数字ID。将数字解包允许从使用Map存储它们的Pool中检索具有该ID的共享不变属性。

剩下的问题是如何处理不再使用的属性值的垃圾回收。

一个已经考虑的策略是使用引用计数;当创建对象并检索某个值的共用标识时,该值的引用计数会递增;当它不再使用时,它必须递减。确保这一点递减

一种选择的情况是添加下面的finalize方法:

public void finalize() { 
    Pool.release(getPropertyId()); 
} 

但是,如果被终结的这个行为意味着对对象的额外指针必须保持,上箭头这个应用程序的前端成本可以被认为是高的。如果这意味着必须分配额外的对象,那么它几乎肯定会太高......因此,我的问题是:可定稿的直接前期成本是多少?

+0

是不是不好的做法,依靠的对象的终结?我的意思是,它不能保证'finalize()'会被调用 - 或者? – vikingsteve

+0

您可能想要管理自己的对象池,而不是依靠JVM。根据您需要同时使用多少个对象,您可以通过完全避免分配/垃圾收集/最终化开销来发现巨大的性能提升。 –

+0

您不应该创建这么多的可终结对象,这应该是一个问题。您应该只将它作为最后的手段使用,否则清理对象时会发现严重的性能/稳定性问题。你可能不会担心,但是使用这个很多,你会。 –

回答

7

终结者是糟糕不仅因为保留问题,而且从性能角度来看。

在Oracle JDK/OpenJDK中,使用finalize方法的对象由Finalizerjava.lang.ref.Reference的子类)的实例支持。

所有终结器在对象构造器的末尾分两步注册:调用from Java to VM,然后调用。 JIT编译器不能内联这种双转换Java-> VM-> Java。但最糟糕的是,Finalizer的构造函数在global lock下创建了一个链表! (facepalm)

终结器在内存占用方面也不好:除了所有引用字段,它们还有two extra fieldsnextprev

PhantomReferences比终结好得多:

  • 他们施工时不需要过渡到VM和背部可内联的;
  • 除了继承java.lang.ref.Reference之外,它们没有额外的字段;
  • 没有进行全局同步。

This benchmark比较终结对象的分配速度和物体的幻影支持:

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 
+0

是否有任何理由不修补openJDK中的全局锁定? – qwwdfsad

+0

@qwwdfsad我会说没有补丁的理由。为什么要关心不推荐使用的遗留代码。 – apangin

相关问题