paulk-asert opened a new pull request, #2820:
URL: https://github.com/apache/groovy/pull/2820
### What this PR is
The GROOVY-12281 investigation branch, opened for review of one shippable
change plus its
supporting evidence. **Only `2ab40501e2` ("soft GroovyClassValue mode — soft
values with
resurrection (opt-in)") is intended to land on master** — ideally
cherry-picked or landed
alone rather than squash-merging the branch. The other commits are
investigation artifacts
that should stay on the branch:
- `45e3fc6a56` / `a429dd7415` — the *hybrid* prototype (measured,
**declined**) and the guide
update quantifying the existing `=false` escape hatch (the guide change is
independently
master-worthy if wanted);
- `12d59e847b` — the assessment document (v2) and the loader/perf spike
harnesses:
[GROOVY-12281-assessment.html](https://github.com/apache/groovy/blob/groovy12281/GROOVY-12281-assessment.html).
### Problem
A `java.lang.ClassValue` association lives as long as its key class, so
`ClassInfo.globalClassValue` entries on immortal platform classes (String,
Integer, …) hold a
strong chain to the ClassInfo and through it to Groovy's class loader —
leaking every Groovy
copy a container deploys and undeploys (JDK-8136353 / GROOVY-12142). The
existing remedy,
`-Dgroovy.use.classvalue=false`, gives up the per-Class fast path for every
key
(~4× on the raw lookup, +4.8% geomean on classic dispatch, hot platform
idioms to 1.38×).
### Change
`-Dgroovy.use.classvalue=soft` keeps `ClassValue` for all keys but stores
each value behind a
bootstrap-loaded `SoftReference`, cutting the only strong chain from
immortal keys to the
Groovy island. Three cooperating pieces make that safe (details and file
references in the
assessment):
- **Resurrection** — a weak-key/weak-value side map is the identity
authority: a value still
reachable anywhere is re-associated, never replaced, so a fresh instance
can only exist once
no guard can still observe the old one. Classic call-site version guards
therefore stay
sound: legacy-compiled jars using `groovy-callsite` from the classpath are
unaffected.
- **Dirty roots** — ClassInfos carrying non-reconstructible state (installed
MetaClass,
per-instance MetaClasses, registry-written DGM/extension arrays) are
strong-rooted
Groovy-side; `ClassInfo.remove()` unroots, keeping its hard-detach
undeploy semantics.
- **Per-Class indy domain continuity** — SwitchPoint domains are keyed by
`Class` in soft
mode, so a successor ClassInfo adopts its predecessor's domain and
mutations
deterministically retire guards that captured only the SwitchPoint (POJO
direct dispatch).
Indy-only; the default mode's behaviour is unchanged.
Default-mode behaviour is byte-identical apart from no-op hook calls; all
soft-mode structures
are unallocated unless the flag is set.
### Evidence
- **Acceptance** (the ticket's point; JDK 17/21/23): a dropped child-loader
Groovy copy is
pinned forever under the default and **collected under soft mode** once
pressure clears
soft references.
- **Correctness**: unit tests + a deterministic child-JVM probe
(resurrection identity/version
continuity, DGM rooting, EMC/per-instance survival, recreation dispatch,
classic
`CallSiteArray` soundness, predecessor-SwitchPoint retirement); a real-GC
concurrency
stress probe (128 MB heap, `SoftRefLRUPolicyMSPerMB=0`): 66M dispatches,
~130 EMC
generations, 33/36 platform-receiver ClassInfos collected and recreated
mid-run, zero
invariant violations; full core suite passes under soft mode (16,800
tests).
- **Cost**: raw `getClassInfo` 1.2 → 1.7 ns; compiler harness +0.0%;
classic-bytecode JMH gate
0 of 37 significant (geomean +1.7%; the declined map default failed the
same rule with 12,
worst 1.38×); classic *polymorphic* miss traffic ≈ +3.7% pooled, flagged
for the idiom-suite
sweep before any default-flip discussion.
### Open questions for review
Assessment §9 consolidates the resulting option landscape
(`true`/`soft`/`false` ×
static/dynamic × indy/classic, with guidance); §10 lists the open items:
adoption path
(opt-in now — recommended — vs eventual default), whether domain re-homing
should become
unconditional rather than soft-gated, and container soak experience. The
integration guide is
deliberately untouched until the mode's fate is decided.
JIRA: https://issues.apache.org/jira/browse/GROOVY-12281 (assessment v2
supersedes v1 in place;
this prototype responds to the review comments there)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]