Do objects with a lot of reference fields (except arrays) devastate Hotspot JVM's GC(s) heap traversal performance?
Asked Answered
B

1

12

Imagine that I define a class with dozens of reference fields (instead of using reference arrays such as Object[]), and instantiate this class pretty heavily in an application.

Is it going to affect the performance of garbage collector in Hotspot JVM, when it traverses the heap to calculate reachable objects? Or, maybe, it would lead to significant extra memory consumption, for some JVM's internal data structures or class metadata? Or, is it going to affect the efficiency of an application in some other way?

Are those aspects specific to each garbage collector algorithm in Hotspot, or those parts of Hotspot's mechanics are shared and used by all garbage collectors alike?

Bora answered 5/3, 2018 at 8:31 Comment(5)
Either your class has the references in its fields, or you have a reference to an Object[] -- which has the references in its slots. Either way, you have an object (yours or the array) wit a bunch of references to other objects. Why would you expect there to be a difference?Pint
@T.J.Crowder I don't know, that's why I ask this question. But I can easily imagine that there could be difference. JVM could treat arrays specially in many ways.Bora
@T.J.Crowder is correct. I've worked extensively in this area in the Hotspot VM. There is no difference for the Hotspot GCs between reference arrays and objects with many reference fields.Arnoldarnoldo
If anything, doesn't this somewhat amount to (manually maintained) 'object layout'? Avoiding the array should help avoid pointer dereferencing required for the data-dependent load when indexing / dead-reckoning.Operose
If you're thinking in C then in Java everything is a pointer. Whenever you see "Object", you're using a pointer. Arrays of objects are arrays of pointers. There is no . operator from C, it looks like . but works like ->. Even == works exactly how comparison of pointers should work. There is no way to control memory layout.Moan
D
13

Let me rephrase the question. "Is it better to have class A or class B, below?"

class A {
  Target[] array;
}

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

The usual maintainability issues notwithstanding... From VM side of view, given the resolved reference to class B, it requires one dereference to reach Target field. While in class A, it requires two derferences, because we also need to read through the array.

The handling of object references in two cases is subtly different: in class A, VM knows there is an contiguous array of references, and so it does not need to know anything else. In class B, VM has to know which fields are references (because there could be non-reference fields, for example), which requires maintaining the oop maps in the class metadata:

//  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 

Note that while footprint overhead is there, it is unlikely to matter very much, unless you have lots of classes of this weird shape, but even then the cost would be per-class, not per-instance.

Oop-maps are built during class parsing, by the shared runtime code. The visitors that walk the "oop"-s for the particular object looks into those oop-maps to find the offsets for references, and that code is also the part of shared runtime. So, this overhead is independent of GC implementation.

Considerations for performance:

  1. Oop-maps are chunked: the runs of adjacent reference fields would form a continuous oop-map block that would be visited pretty much like we would with continuous oop block in reference array.
  2. The GC (marking) performance is dependent on the number of references it has to follow, and memory latency on dereferences would be the first-order effect. Note that in class A, we have to traverse more references.
  3. The null-checks and array bounds checks would probably matter in class A case, if requested indices are not constant and array lengths are not known on critical code paths. In comparison, fields are bound statically, and their offsets are always known.

So, it probably makes little sense to ask about the difference in GC/runtime handling of separate fields vs arrays. Taking care of locality of reference quite probably gives a bigger bang for the buck. Which tips the scale to class B, with associated maintainability overheads -- as quite a few performance tricks do.

Disjoin answered 5/3, 2018 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.