[PATCH 0/2] Class- and class loader-local storage (Bug ID #6493635)
David M. Lloyd
david.lloyd at redhat.com
Tue Mar 3 06:37:53 UTC 2009
On 03/02/2009 10:45 PM, Bob Lee wrote:
> Here's the problem I'd like to see solved: enable a library to hold an
> indirect reference to a Class without preventing that Class's loader
> from being reclaimed. The reclamation should happen automatically when
> the loader is not otherwise strongly referenced.
With you so far.
> You added a further requirement: the class-local value should not
> prevent the library's loader from being reclaimed. For example, if code
> loaded in a child class loader stores a value (which strongly references
> a Class in the child loader) in a Class from the parent loader, the
> class-local value in the parent-loaded Class should not prevent the
> child loader from being reclaimed.
That is a natural consequence of the key-value design, though not exactly
in the way you describe. The child-classloader-value is obviously strongly
reachable from the parent loader's "stash", so it's not immediately
collectable until the key is collected. If one associates a key with an
instance of the child class, then this automatic reclamation can happen
automatically. If not, then there is no automatic solution possible, even
with special VM ephemeron support (this basically IS an ephemeron
implementation, after all, albeit a specialized one).
> Your solution is to explicitly clear the class-local value (for every
> class regardless of its loader), but doesn't that fail to solve the
> original problem? If you're going to explicitly clear anyway, why do you
> need this mechanism in the first place?
No, it compliments the solution of the original problem. You're only going
to explicitly clear if you want to remove the value *before* the
classloader is reclaimed (the one upon which the value is stashed).
Think of it as two ways to reclaim the data. The value (normally strongly
referenced from the "stash") is unreferenced when either:
1) The classloader upon which it is stashed is no longer strongly reachable, or
2) The reference is explicitly cleared.
The reason you need that ability to explicitly clear a reference is simple.
Imagine you're using the bare mechanism you describe with a Map<Class<?>,
?> that has weak keys and weak values (relying on the reference-stashing to
keep the strong value reference) in order to make a weak-key mapping. What
if you want to change the map? Remove an entry or change its value? You
can stash a reference to the new value object, but the old one is still
there, forever (or at least until the classloader is collected, which for
the system classloader, is essentially forever). It's a guaranteed memory
leak. This is where removal comes into play.
One very sweeping use case that fits this description involves frameworks
which use runtime annotations, building up (possibly complex) object
structures for each annotated class it encounters. In many cases, this
data might need to be cached to avoid possibly expensive reconstructions of
that object structure for each class, yet the framework cannot directly
retain references to any single classloader using it - at least not without
risking the dreaded PermGen memory leak when its consumers were redeployed.
The nature of the basic implementation ensures that this won't happen.
If for some reason circumstances change, and the cached data needs to be
updated (say, perhaps, that some new information became available about a
database or remote system or whatever, which relates specifically to
whatever service that framework provides to that class), the framework is
free to do so by manually clearing or updating the value of the reference.
Now if that *framework* were to be undeployed, the application server will
drop its strong reference to the object of the deployment, which in turn
would typically lead to the key object becoming only weakly reachable.
This will automatically remove all of the framework's stashed key/value
pairs, and ultimately allow the framework's classloader to be collected.
If that is *not* the case, and the reference situation is more complex,
then one is still free to manually clear the key during the undeployment
> When I started this thread, I was content with solving the simpler case:
> a library loaded in the parent class loader associating data with
> classes loaded in a child loader. This is solvable at the library level
> and doesn't require explicit clearing. If I want a library loaded in the
> child class loader to store information about classes from the parent
> loader, I can just keep strong references to the Class objects because I
> know that the child loader will be reclaimed before the parent loader.
When would that situation actually arise, just out of curiosity? In terms
of specific use case I mean.
> Your problem can't be solved at the library level (without explicit
> clearing, which defeats the purpose of this construct). Ephemerons are
> the only viable solution that I know of.
I think you're confusing the requirement for explicit clearing when the
value changes or becomes obsolete with automatic collection of the keys.
Implicitly clearing the value in this situation can *never* work - not with
ephemerons, not without a psychic GC - because the application is always
holding only a weak reference to the value *already*. The whole point was
to add a strong reference to the value from the classloader; how can the GC
determine when you don't need that particular value anymore?
With my solution (and also with an ephemeron-based solution), as long as
you hold a strong reference to the key, the value can exist. The key
becomes something which acts just like a strong reference to the value, but
without actually preventing the classloader (and thereby the value) from
being collected if it is no longer referenced. And that still holds true
even if the value has a strong reference to that classloader.
More information about the core-libs-dev