GROOVY-12280: hold cached awaitable adapters through SoftReferences - #2818
Merged
Conversation
blackdrag
reviewed
Aug 19, 2026
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2818 +/- ##
==================================================
+ Coverage 70.1794% 70.1852% +0.0058%
- Complexity 35853 35882 +29
==================================================
Files 1563 1563
Lines 132546 132575 +29
Branches 24385 24397 +12
==================================================
+ Hits 93020 93048 +28
Misses 31113 31113
- Partials 8413 8414 +1
🚀 New features to boost your workflow:
|
This comment has been minimized.
This comment has been minimized.
A ClassValue association lives as long as its key class, and the common keys here are platform classes such as CompletableFuture, so the cached Groovy-loaded adapter pinned Groovy's class loader for the lifetime of the JVM (JDK-8136353 behavior, working as intended). The value is now held through a SoftReference: the association strongly reaches only java.base objects. A cleared reference is removed and recomputed once; should the fresh reference already be cleared, the answer comes from an uncached scan, so lookups terminate under any memory pressure. A NO_ADAPTER sentinel keeps unsupported types distinguishable from cleared references. The site deliberately stays on java.lang.ClassValue rather than routing through GroovyClassValueFactory: with soft values nothing strongly Groovy-loaded remains in the association, so there is nothing left for the groovy.use.classvalue escape hatch to release here.
paulk-asert
force-pushed
the
groovy12280
branch
from
August 19, 2026 09:02
6c11ed7 to
359464e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A ClassValue association lives as long as its key class, and the common keys here are platform classes such as CompletableFuture, so the cached Groovy-loaded adapter pinned Groovy's class loader for the lifetime of the JVM (JDK-8136353 behavior, working as intended). The value is now held through a SoftReference: the association strongly reaches only java.base objects. A cleared reference is removed and recomputed once; should the fresh reference already be cleared, the answer comes from an uncached scan, so lookups terminate under any memory pressure. A NO_ADAPTER sentinel keeps unsupported types distinguishable from cleared references.
The site deliberately stays on java.lang.ClassValue rather than routing through GroovyClassValueFactory: with soft values nothing strongly Groovy-loaded remains in the association, so there is nothing left for the groovy.use.classvalue escape hatch to release here.
Soft references are cleared by the collector under heap pressure, so the class loader becomes collectible rather than instantly collected — the designed semantics of the strategy agreed in the GROOVY-12142 review discussion, not a gap.
The change was also verified against the groovy-concurrent-java deliverable, whose jar is a filtered extraction of core promising Java users no Groovy-runtime dependency: jdeps over the registry classes with only that jar on the classpath resolves every reference from java.base or the jar itself (SoftReference and ClassValue are java.base). That check is a second, independent reason the factory-routing alternative was declined — GroovyClassValueFactory lives in org.codehaus.groovy.reflection, outside the extraction set, so routing through it would have shipped the extracted jar with an unresolvable reference.