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

Java 具有大量引用字段(数组除外)的对象是否会破坏热点JVM';s GC(s)堆遍历性能?,java,performance,garbage-collection,jvm,jvm-hotspot,Java,Performance,Garbage Collection,Jvm,Jvm Hotspot,假设我定义(而不是使用引用数组,如Object[]),并在应用程序中大量实例化此类 在Hotspot JVM中,当垃圾收集器遍历堆以计算可访问对象时,它会影响垃圾收集器的性能吗?或者,对于某些JVM的内部数据结构或类元数据,这可能会导致大量额外的内存消耗?或者,它是否会以其他方式影响应用程序的效率 这些方面是否特定于Hotspot中的每个垃圾收集器算法,或者Hotspot机制的那些部分是由所有垃圾收集器共享和使用的?让我重新表述一下这个问题。“下面是A级还是B级比较好?” 尽管存在常见的可维护性

假设我定义(而不是使用引用数组,如
Object[]
),并在应用程序中大量实例化此类

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


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

让我重新表述一下这个问题。“下面是A级还是B级比较好?”

尽管存在常见的可维护性问题。。。从VM的角度来看,给定对类B的已解析引用,它需要一次解引用才能到达目标字段。在类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映射是在类解析期间通过共享运行时代码构建的。浏览特定对象的“oop”-s的访问者会查看这些oop映射以找到引用的偏移量,该代码也是共享运行时的一部分。因此,此开销独立于GC实现

性能注意事项:

  • Oop映射是分块的:相邻引用字段的运行将形成一个连续的Oop映射块,它将被访问,就像我们在引用数组中使用连续的Oop块一样
  • GC(标记)性能取决于它必须遵循的引用数,而解引用的内存延迟将是一阶效应。注意,在类A中,我们必须遍历更多的引用
  • 如果请求的索引不是常量,并且关键代码路径上的数组长度未知,则null检查和数组边界检查在类A中可能很重要。相比之下,字段是静态绑定的,其偏移量总是已知的

  • 因此,询问单独字段和数组在GC/运行时处理方面的差异可能没有什么意义。考虑引用的局部性很可能会带来更大的收益。这会导致扩展到B类,并带来相关的可维护性开销——正如许多性能技巧所做的那样。

    要么您的类在其字段中有引用,要么您对其插槽中有引用的
    对象[]
    。无论哪种方式,您都有一个对象(您的对象或数组),其中包含一组对其他对象的引用。为什么你会期望有不同?@T.J.Crowder我不知道,这就是为什么我问这个问题。但我可以很容易地想象,可能会有不同。JVM可以在许多方面特别处理数组。@T.J.Crowder是正确的。我在Hotspot VM的这一领域做了大量工作。热点GCs在引用数组和具有多个引用字段的对象之间没有区别。如果有区别的话,这不就是(手动维护的)“对象布局”吗?避免数组应该有助于避免索引/航位推算时数据相关负载所需的指针解引用。如果您使用C语言思考,那么在Java中,一切都是指针。每当你看到“对象”时,你都在使用指针。对象数组是指针数组。C中没有
    操作符,它看起来像
    ,但工作起来像
    ->
    。即使是
    ==
    ,指针的比较也应该如此。无法控制内存布局。
    //  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