具有大量引用字段(数组除外)的对象是否会破坏Hotspot JVM的GC堆遍历性能?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了具有大量引用字段(数组除外)的对象是否会破坏Hotspot JVM的GC堆遍历性能?相关的知识,希望对你有一定的参考价值。

想象一下,我定义了a class with dozens of reference fields(而不是使用Object[]之类的引用数组),并在应用程序中大量实例化这个类。

是否会影响Hotspot JVM中垃圾收集器的性能,当它遍历堆来计算可达对象时?或者,对于某些JVM的内部数据结构或类元数据,它可能导致显着的额外内存消耗?或者,它是否会以某种其他方式影响应用程序的效率?

那些特定于Hotspot中的每个垃圾收集器算法的方面,或者Hotspot的机制的那些部分是否被所有垃圾收集器共享和使用?

答案

让我重新解释一下这个问题。 “下面有A级或B级更好吗?”

class A {
  Target[] array;
}

class B {
  Target a, b, c, ..., z;
}

尽管存在通常的可维护性问题......从VM方面来看,鉴于对B类的解析引用,它需要一个取消引用才能到达Target字段。在A类中,它需要两个差异,因为我们还需要读取数组。

在两种情况下对象引用的处理略有不同:在类A中,VM知道存在连续的引用数组,因此它不需要知道任何其他内容。在B类中,VM必须知道哪些字段是引用(例如,因为可能存在非引用字段),这需要在类元数据中维护oop映射:

//  InstanceKlass embedded field layout (after declared fields):
...
//    [EMBEDDED nonstatic oop-map blocks] size in words = nonstatic_oop_map_size
//      The embedded nonstatic oop-map blocks are short pairs (offset, length)
//      indicating where oops are located in instances of this 

请注意,尽管存在足迹开销,但它不太可能非常重要,除非您有许多这种奇怪形状的类,但即使这样,成本也是每个类,而不是每个实例。

Oop-map是在类解析期间通过共享运行时代码构建的。遍历特定对象的“oop”-s的访问者查看那些oop-map以查找引用的偏移量,并且该代码也是共享运行时的一部分。因此,此开销与GC实现无关。

绩效考虑因素:

  1. Oop-map是分块的:相邻参考字段的运行将形成一个连续的oop-map块,其访问方式与参考数组中的连续oop块非常相似。
  2. GC(标记)性能取决于它必须遵循的引用数量,并且取消引用的内存延迟将是一阶效应。请注意,在A类中,我们必须遍历更多引用。
  3. 如果请求的索引不是常量且在关键代码路径上不知道数组长度,则在A类情况下,空检查和数组边界检查可能很重要。相比之下,字段是静态绑定的,并且它们的偏移量始终是已知的。

因此,询问单独字段与数组的GC /运行时处理的差异可能没什么意义。照顾参考地点很可能会带来更大的收益。这会将规模提升到B类,并带来相关的可维护性开销 - 正如相当多的性能技巧所做的那样。

以上是关于具有大量引用字段(数组除外)的对象是否会破坏Hotspot JVM的GC堆遍历性能?的主要内容,如果未能解决你的问题,请参考以下文章

协议缓冲区:更改字段名称会破坏消息吗?

为什么我可以使用数组破坏性赋值给一个引用,而不是对象破坏性赋值?[重复]

Angular @Input 数组 - 如何引用不同的数组对象字段名称?

Jolt 将多个对象转换为具有新字段名称的数组

持有C ++引用是否可以防止变量被破坏?

引用与复制