I think at this point I have enough understanding of what's going on
to file an issue, and create a PR with a fix. I also have a diagnostic
test, (albeit not minimal enough to be committed) which currently only
fails on J9. It's still unclear however whether I have enough buy-in
from the community on the existence of the issue to get any PR I
create merged. What would be the best next step here?

Chris

On Tue, 10 Feb 2026 at 12:08, Chris Dennis <[email protected]> wrote:
>
> On Tue, 10 Feb 2026 at 06:27, Jochen Theodorou <[email protected]> wrote:
> >
> >
> >
> > On 2/9/26 22:33, Chris Dennis wrote:
> > > On Mon, 9 Feb 2026 at 15:04, Jochen Theodorou <[email protected]> wrote:
> > >>
> > >> On 02.02.26 17:50, Chris Dennis wrote:
> > [...]
> > > The lazy mechanism is working correctly - the problem is that the
> > > lazily created instances are only softly-referenced which means the GC
> > > can come along later and clean that reference, and then a newly
> > > arriving thread will create an additional instance of CachedClass for
> > > the same type. If/when those two instances then meet they will falsely
> > > compare not-equal and the Groovy runtime will think they represent
> > > different types (which they don't).
> >
> > Maybe what happens is that the class is ready to be collected, but then
> > we use ClassInfo to get the instance, while at the same time we are
> > creating a new instance? Just trying to figure out why two instances
> > even exist.
>
> The current mechanism I can see that allows you to end up with two
> instances is because a ClassInfo softly references its CachedClass via
> `ClassInfo.cachedClassRef` but CachedClass instances also softly
> reference the CachedClass instances corresponding to the parameter
> types of their methods via the `CachedClass.methods` field. When the
> ClassInfo.cachedClassRef reference is cleared by the GC we are primed
> for the construction of a new instance, but the old instance can still
> be accessible via the `CachedClass.methods` field of any other type
> with a method that takes that type as a parameter. In a more abstract
> sense we can't rely on the current scheme to prevent multiple
> instances from existing while we allow the instances to be
> softly-reachable via paths involving more than one instance of
> soft-reference, because those instances will not all be cleared at the
> same time.
>
> >
> >
> > > I think the fix here is to
> > > implement (and use) an equals method for CachedClass which uses this
> > > referential comparison but only as an optimistic fast path.
> >
> > that is a workaround for me though... well... it depends on the
> > conditions we want those constructs to fullfill
>
> To me this isn't a workaround, but an acceptance that we cannot
> reliably maintain the 1:1 relationship between ClassInfo and
> CachedClass - and that therefore the equality contract between them
> cannot be a referential one.
>
> >
> > > (There is
> > > another theoretical fix here where all accesses of a given CachedClass
> > > are always mediated through a single SoftReference, which I think
> > > would make the existing scheme safe, but I fear it would be overly
> > > brittle).
> >
> > This sounds a lot like the soft reference will reference an instance,
> > that only this soft reference will reference. Which would be bad
>
> It's not bad... since the soft reference will not be cleared while the
> referent is strongly referenced. So the CachedClass instance could not
> be replaced while there was a strong reference to it that its future
> equivalent could be compared with. I cannot think of an easy way of
> preventing someone from breaking such a system though, (even if only
> accidentally), so I think it's not worth the risk.
>
> >
> > >>> I'm attempting to narrow down how exactly this is happening and whether
> > >>> the cause is OpenJ9 incorrectly clearing a soft reference to a strongly
> > >>> reachable instance, or is due to Groovy missing one or more
> > >>> reachabilityFences to prevent early clearing of these references.
> > >>
> > >> Can you verify other JVMs as well?
> > >
> > > I've not been able to reproduce this on anything other than OpenJ9 -
> > > which I suspect is due to OpenJ9 being much more eager to clear
> > > references. I'm pretty sure I have a valid mechanism through which it
> > > can happen though - it just seems to be impossible to make Hotspot
> > > trigger it (so far).
> >
> > Haven't worked with the eclipse/IBM JVm for many years (since Java 9 or
> > so), but I do remember having regularily trouble with the references
> > stuff on there.
> >
> > bye Jochen
> >
>
> Chris

Reply via email to