Actually java.lang.Class does not implement hashcode and equals. This is
fine, it means that it uses the java.lang.Object methods which is exactly
what is required here. This also means that it is a fairly fast hash key.
If we reduce the cache to one level and use Class as a key, we will get a
faster cache.

I think that using the class loader as cache key is unsafe.
Take for example the structure of an ear file. In most app server every ear
file will translate to at least three levels of class loaders (on top of the
three+ provided by the VM). To reloaded it, you simply replace the ear class
loader. This triggers the entire chain. If the cache keeps all the three
levels of class loaders as keys, you have to explicitly call clear on each
one of them. Calling clear on the top most ear class loader is not enough.
The cache references its children that reference it, so it cannot be garbage
collected. The only safe thing here is to clear the entire cache.

Another thing is practicality:
When you develop an application, you don't have access to old class loaders.
Usually dynamically loaded applications will just replace a class loader. In
a lot of cases this process does not have any knowledge of the old class
loader and definitely about the children that it created. It simply cannot
be responsible for old class loaders. In a lot of cases there are class
loaders created on the fly for specific tasks. Requiring the objects that
use them to be responsible for cleaning after them is not always possible.
It is much easier if the creation process actually refresh the whole thing. 

Think what would happen if you write an application that has dynamic class
loading and an open API. You do things properly when you refresh your
classes, you clean your class loader. But then users create their own class
loaders through the API. Today, most APIs create class loaders. It is
unreasonable to ask every person that use your API to clear your cache after
it creates a class loader. Especially if you use third party packaged APIs,
like a JDO implementation that manages its own class loaders for beans.

About performance:
This process happens at development time, and in a very small frequency when
deploying applications. At development time, nuking an entire cache is
meaningless.
At actual production runtime it will not happen, so you do not pay any
price. In that case you want a fast cache, which is what you get with one
level cache (and by not using String as cache keys). what is important to
the runtime is a safe implementation. 
Memory leak with class loader will slow down the VM and eventually crash it.
This is not good not in design time and not at production.
What is also interesting is that cache algorithms that clear entire caches
(or clear random element from the cache, which is basically the same thing)
usually have the same complexity of their competing, more sophisticate
cousins. In reality, their code is much simpler and efficient than their
complicated cousins, and they are more robust in terms of memory leak bugs.

The introspector class has been abound for a while, all we have to do is
follow the same path as the JDK.
The way I see it:
Using ClassLoader is unsafe (create a misleading API)
Using ClassLoader is slower (two level cache, using String as a cache key)

Speaking of java.beans.Introspector, I think that clearing our cache should
call flush on the introspector cache, that is another potential memory leak
:-)

E

> Date: Wed, 5 Sep 2001 00:58:19 -0700
> From: Elli Albek <[EMAIL PROTECTED]>
> Reply-To: [EMAIL PROTECTED]
> To: [EMAIL PROTECTED]
> Subject: Re: BeanUtils and class loading + EJB comment
>
> how about this caching strategy:
>
> The class java.beans.Introspector is using the Class object as the cache
> hashtable key.

It is actually now a two level key -- effectively it is ClassLoader +
Class Name.  This is required to make sure that the same class, loaded
from a different class loader, has its own introspection informatio.

> This is fine, two Class objects with the same name but with different
class
> loader do not return true for equals().

True ... they are totally independent of each other, and not even castable
or assignment compatible.

> If we use that as the key, than one cache can contain all the beans.

I agree that using the Class object itself as the key would be unique,

> The
> problem becomes when class loaders are created repeatedly, like in the
case
> of JSP page development. In that case, if the programmers do not call the
> clear functions there will be a memory leak.
> What java.beans.Introspector is doing is to key by the Class, and have two
> flush functions, one for the entire cache, and one for a specific class.
> This can work here as well. If we need to flush by a class loader (I don't
> think it is necessary, repeated class loading happens only at development
> time, there is no real problem in nuking all the cache) it is still
possible
> to iterate over all the keys and remove those that come from that class
> loader.
>
> Another option is to make the cache a WeakHashMap. In that case the class
> will eventually expire, and the class loader can be freed.
> I am not sure whether WeakHashMap or SoftRefHashMap is more suitable here.
> Since we deal with class objects, the issue is much more complicated. The
> class org.apache.commons.collections.SoftRefHashMap will not be helpful
> here. The cached values will expire, but the keys (the actual Class
objects)
> stay in the map. In that case the class is still referenced and the
> ClassLoader cannot be garbage collected.
> java.util.WeakHashMap is also not a sure bet. The weak reference is
supposed
> to be cleared after all other strong references are cleared. I am not sure
> how that can happen with Class Objects. The class loader will maintain a
> reference to them. The class loader cannot be released until all the Class
> objects are, but they are still reachable through a weak reference (so the
> class loader cannot release). This becomes a chicken and an egg problem.
>
> So I think the safest is to use the java.beans.Introspector approach, it
> seems the be the safest. I think using the class loader as a key might
cause
> some unexpected behaviours, because of the parent child relationships of
> class loaders. probably the safest thing is to key by class and nuke the
> whole cache all together. Class loaders also do not implement hashcode()
and
> equals(). This also makes them suspects for table keys. (though the Object
> class hashcode() and equals() are supposed to be enough in that case).
>

Nuking the entire cache is certainly feasible -- just call the clear
method with no arguments.  However, it seems to me that it's overkill in
most circumstances.  I don't see that the parent/child relationships would
make any difference - if I know that I'm throwing away a particular class
loader, I would want to throw away just the objects loaded by that class
loader, and keep the rest.  Hence, I think it is appropriate to offer both
method signatures (indpendent of whatever choice we make for the internal
representation).

Regarding using class loaders as keys, the features we need are that
* The same (class loader) object always returns the same hash code value
* No two (class loader) objects return the same hash code value

The first property is required by the contract for the hashCode() method,
so the default implementation in java.lang.Object will provide it.  The
second is not guaranteed, but in practice seems to be provided by JVMs I
have used when using arbitrary objects as keys.

That being said, keying a single-level cache by Class would solve this (as
long as java.lang.Class implements hashCode() and equals() correctly :-).

> E
>

Craig

Reply via email to