some thoughts on ClassValue
Peter Levart
peter.levart at gmail.com
Fri Apr 26 05:44:54 PDT 2013
On 04/26/2013 09:59 AM, Jochen Theodorou wrote:
> Hi all,
>
> basically ClassValue is there so that my runtime/framework or whatever
> can store information and bind it to a class. The information is then
> available as long as the class exists. In Groovy3 I am going to use this
> for meta classes.
>
> Some time ago a user came up with a special need, he wanted to be able
> to replace the groovy runtime at runtime but could not do so because of
> classes being referenced that keep classloaders alive, which then lock
> the jar on the file system on windows.
>
> This did let me to the thought, that as soon as I start storing meta
> classes using ClassValue, my Groovy runtime will never unload again as
> soon as I store for the example a meta class on ArrayList (which is
> likely to happen) or Object, or any other java core class for that
> matter. This makes me think that I should not directly use ClassValue to
> enable unloading... but I also do not want unloading to happen when I
> still need the value.
>
> Does anyone have any idea on now to solve this problem?
>
> bye blackdrag
>
Hi Jochen,
It seems you have similar issues that for example j.l.r.Proxy has. It
spins a class implementing given interfaces, defines it with a given
ClassLoader and then caches it for future-use. The situation with
j.l.r.Proxy is simpler than your's, I assume. Could you describe in some
more detail, what are "meta classes" in groovy runtime in terms of how
they are generated (lazily at runtime or ahead of runtime?), what
dependencies they can have and who is dependent on them. I assume they
are generated at runtime, since if they were accessible as class files
on the classpath of some loader, you would not have to design your own
caching/loading/defining layer (Class.forName() would do). That's still
an option if you create your own ClassLoader subclass that would use a
naming convention (say groovymeta.some.pkg.ClassName would be the name
of meta class "belonging" to some.pkg.ClassName main class) and use that
ClassLoader as the class loader for groovy apps. But that's hard to
enforce in various environments... So dynamic generation then.
You typically would want to define a class with the ClassLoader that
also "sees" all the dependencies of the class. But there could be
several ClassLoaders in a runtime that are suitable in a given
situation. You would choose the right one depending on the lifetime of
the generated class and the possible users of that class. And you would
want the Class object of the "meta class" to be cached via a
WeakReference<Class>, so that when all the users of the class are gone
the class itself becomes one step to eligible to GC. It will not be
GC-ed until the ClassLoader that defined it becomes eligible to GC
(since the ClassLoader maintains explicit hard-references to all the
Class objects for classes that it has defined). And each class maintains
an implicit reference to the ClassLoader that defined it. So when all
the users of all classes defined by a single ClassLoader are gone and
the ClassLoader is not reachable any more, the whole graph of
ClassLoader with all it's Class objects is GC-ed. It's that simple.
The question is how to most effectively design the structure so that you
can quickly obtain the cached Class object and use the class represented
by it. One part of the lookup "key" seems to be the "main class" that
the "meta class" belongs to. So ClassValue is one option. But I can
imagine you would want to support groovy runtime in environments such as
app servers where the groovy runtime is deployed at the system level and
the contents of a metaclass belonging to some system class (say
ArrayList) is supplied by the deployed application and there could be
several deployed applications, each contributing it's own different
"meta class" for ArrayList. So the main class is not the only "key" for
lookup. I would imagine the ClassLoader of the deployed application is
the other part of the "key" and you would want to define a separate meta
class with each application ClassLoader for the same main class. So the
most general approach would be the structure similar to the following:
Map<WeakReference<ClassLoader>, ClassValue<WeakReference<Class>>>
^ ^
| meta class
the defining class loader of the meta class
which is equivalent to:
Map<WeakReference<ClassLoader>, Map<WeakReference<Class>,
WeakReference<Class>>>
^ ^ ^
| | meta class
| main class
the defining class loader of the meta class
... with the hashCode/equals of the WeakReferences suitably overriden
of course.
Regards, Peter
More information about the mlvm-dev
mailing list