[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:
> David,
> 
> 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 
process.

> 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.

- DML




More information about the core-libs-dev mailing list