You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Paul King (Jira)" <ji...@apache.org> on 2026/08/24 21:03:00 UTC
[jira] [Resolved] (GROOVY-12281) Investigate mitigating class-loader pinning by ClassInfo.globalClassValue under the default ClassValue implementation
[ https://issues.apache.org/jira/browse/GROOVY-12281?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Paul King resolved GROOVY-12281.
--------------------------------
Fix Version/s: 6.0.0-beta-4
Resolution: Fixed
> Investigate mitigating class-loader pinning by ClassInfo.globalClassValue under the default ClassValue implementation
> ---------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12281
> URL: https://issues.apache.org/jira/browse/GROOVY-12281
> Project: Groovy
> Issue Type: Improvement
> Reporter: Paul King
> Assignee: Paul King
> Priority: Major
> Fix For: 6.0.0-beta-4
>
> Attachments: GROOVY-12281-Assessmentv2.pdf
>
>
> Follow-up from review discussion on [PR #2798|https://github.com/apache/groovy/pull/2798] (GROOVY-12142), preserving the analysis from that review. A {{ClassValue}} realizes the chain _key class → association → value → everything reachable from it_, and the association lives as long as the key class ([JDK-8136353|https://bugs.openjdk.org/browse/JDK-8136353], working as intended). The {{ClassValue}} implementation class itself prevents no unloading; what matters is the key's origin: a Groovy-loaded key class dies with Groovy's loader (fine), but a JDK/platform key class is effectively immortal, so any value reachable from it that was loaded by Groovy's loader pins that loader forever. Indirection counts — a JDK-typed value (e.g. an {{ArrayList}}) whose _elements_ are Groovy-loaded re-creates the pin one level down.
> {{ClassInfo.globalClassValue}} is static with unknown keys, including platform classes ({{String}} receives a {{ClassInfo}} in essentially every Groovy program), so the default {{ClassValue}} path pins the loader; today's only remedy is the global {{groovy.use.classvalue=false}} escape hatch, which trades away the per-class fast path for all keys.
> The general mitigation — {{SoftReference}}-wrapped values with a check-remove-recompute protocol — requires that recomputation be legal, and for {{ClassInfo}} it is not in general: a {{ClassInfo}} can carry non-recomputable state (modified metaclasses, category state), so a softly-collected value could silently discard user metaclass customizations. Approaches to investigate:
> * soft values while a {{ClassInfo}} is pristine, hardening the reference on first mutation — only classes with customized metaclasses would then pin, a far smaller set;
> * per-key policy: platform-loader keys get soft/map treatment, Groovy-loader keys stay strong (the key-origin rule applied mechanically);
> * splitting {{ClassInfo}} into recomputable and stateful parts;
> * revisiting whether the map-based implementation should become the default, with {{ClassValue}} as the opt-in fast path.
> Recompute cost and dispatch-path performance need measurement for any candidate.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)