[PATCH 0/2] Class- and class loader-local storage (Bug ID #6493635) (original) (raw)

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.



More information about the core-libs-dev mailing list