[suggestion] no inline forwarding pointer for shallowly immutable objects

Piotr Tarsa piotr.tarsa at gmail.com
Sat May 15 19:14:21 UTC 2021

As a side note: this thread became more GC oriented than Lilliput oriented.
At first I thought it will look otherwise, but after several reformulations
of my ideas and watching of Shenandoah presentations it became what it its

sob., 15 maj 2021 o 21:03 Piotr Tarsa <piotr.tarsa at gmail.com> napisał(a):

> Hmm, could be I forgot to write something. The main idea here is to use
> one strategy for objects with guaranteed shallow immutability (fwdptrs not
> overlapping with old copies contents, e.g. fwdptrs outside objects) and
> different strategy for others (fwdptr overlapping on old copy, i.e. the
> Shenandoah 2.0 approach, IIUC) and that's the assumption in all the
> proposals here (let's call it "main assumption").
> AFAIU the forwarding pointer is used not only by GC threads to track
> relocations, but also by application threads to make sure that the old copy
> contents are not accessed after switching to new copy. Accessing old copy
> would e.g. cause stale data to be read or cause modifications to old copy
> be discarded. But for shallowly immutable objects it's not a concern, as
> old copy and new copy will be identical anyway (forever). Therefore, at
> least the application threads could ignore the forwarding pointers when
> accessing immutable objects (when not using their identity related
> features) and therefore work faster (at least that's my not very educated
> guess) by accessing the first copy they have pointer to and using it
> directly - that assumes forwarding pointers don't overlap with old copy
> contents (as in the main assumption). So the potential performance
> improvement of application threads is one idea here and most interesting to
> me - what do you think: is the performance improvement potential here
> substantial or not?
> An example for the above would be an immutable singly linked list made of
> https://en.wikipedia.org/wiki/Cons nodes represented as Java records.
> Let's assume a GC relocates the list and, at the same time, application
> threads iterate through that list. As long as application code doesn't use
> identity related features (like comparing references, computing identity
> hash codes, synchronizing on that objects, etc) it doesn't matter whether
> an application thread accesses an old copy or a new (relocated) copy of
> that list.
> As for the idea (also about shallowly immutable objects in general):
> > Maybe keeping the forwarding pointers for such objects in lazily filled
> side tables would reduce overall memory overhead while keeping performance
> overhead relatively low?
> That would also apply to situation where the main assumption (metioned at
> the beginning) holds, so fwdptr link is not overlapped on old copy.
> Therefore old copy could still be used, but it wouldn't have the size
> penalty of having a permanent separate fwdptr slot as the fwdptr would be
> temporarily allocated in a side table (during GC cycle over a region
> containing that particular object). Since that side table would be rarely
> accessed by application threads (i.e. assuming that identity related
> features are used rarely), the performance overhead should be low.
> As I've said, I'm no expert, I have vague understanding of concurrently
> compacting GCs and maybe my interpretations and ideas here don't make much
> sense, but maybe I'll learn something :) Sorry for confusion.
> sob., 15 maj 2021 o 15:58 Roman Kennke <rkennke at redhat.com> napisał(a):
>> > Hi,
>> >
>> > I'm not an expert in JVM internals, so it's more of a question than an
>> > advice.
>> >
>> > On https://wiki.openjdk.java.net/display/lilliput there are already
>> some
>> > ideas about removing identity hash code field for shallowly immutable
>> > objects. The contents of the wiki are:
>> >> We can also reduce the size of the header for certain kind of classes,
>> by
>> > example for a record, we know that the field are truly final so we can
>> > avoid to compute the hashCode and use the fields to calculate the
>> identity
>> > hashCode the same way Valhalla does for the primitive classes.
>> >> For a primitive class, when they are on the heap, again, we can avoid
>> the
>> > identity hashCode (and also the lock bits, but that's less
>> interresting).
>> >
>> > I think we can similarly remove forwarding pointers, at least for boxed
>> > primitive objects, as there may be many copies of a single identity-less
>> > shallowly immutable object and that won't break anything (there's no
>> way to
>> > differentiate between the copies anyway). That could potentially reduce
>> the
>> > header size of such boxed primitive object to just the 32-bits that are
>> > needed for keeping compressed class pointer (and nothing else). Maybe
>> the
>> > age bits (used in generational GCs) are not really needed for certain
>> types
>> > of objects, e.g. primitive objects that contain no references? This way
>> the
>> > smallest data carriers on heap would have just 8 bytes size (e.g. boxed
>> > byte, short, char, int, float).
>> >
>> > I was thinking for a while that forwarding pointer would also be
>> unneeded
>> > (and without replacement) for other types of shallowly immutable
>> objects,
>> > i.e. records and also frozen arrays (if they get accepted, the draft
>> JEPs
>> > are: https://bugs.openjdk.java.net/browse/JDK-8261007
>> > https://bugs.openjdk.java.net/browse/JDK-8261099 - BTW I think they
>> should
>> > be mentioned on the Lilliput wiki page), but then realized that they
>> (can)
>> > have identity, so it's required to know (using the forwarding pointer)
>> the
>> > true single identity of them (to compare addresses or lock on them for
>> > example). However, how often identity is used? Maybe keeping the
>> forwarding
>> > pointers for such objects in lazily filled side tables would reduce
>> overall
>> > memory overhead while keeping performance overhead relatively low?
>> > Accessing a frozen array shouldn't require (I think) using the
>> forwarding
>> > pointer as both copies (if GC make a copy) of frozen array are shallowly
>> > identical anyway. Same goes for records as they are also (if I
>> understand
>> > correctly) guaranteed to be shallowly immutable.
>> The purpose of forwarding pointers is to support GC: when the GC
>> relocates an object, it needs to temporarily keep record of the new
>> location, until all references to the old location have been updated. I
>> don't think that this has anything to do whether or not an object is
>> immutable or have identity. Or maybe I misunderstood what you are
>> getting at?
>> Thanks,
>> Roman

More information about the lilliput-dev mailing list