dev@glassfish.java.net

WeakHashMap and ReadWrite lock

From: Ken Cavanaugh <ken.cavanaugh_at_oracle.com>
Date: Sun, 7 Nov 2010 13:28:46 -0800

A common problem that applications have that need to cache information
about classes is that a cache key that strongly references a Class
(and thus ClassLoader) will pin a large amount of memory in the heap,
especially in an app server that frequently undeploys applications.
Such caches often require multithreaded access, and thus can
potentially become concurrency hot spots. This is especially noticeable
to me in the ORB and the recent trade2 performance issues (see
GF issue 14455).

Most such caches are read far more often than written (perhaps ridiculously so:
the ORB may copy objects of a particular class millions of times, but only need
to update the class-related information once). So the obvious solution
is to employ read/write locking in the code that manages the cache.
Such caches typically contain some sort of Map<Class<?>,CachedData>.
Because of the ClassLoader pinning problem, this map must be a WeakHashMap.

Unfortunately, WeakHashMap has the peculiar characteristic that a Map.get( key )
call MAY update the map. This is because every get call checks to see whether
the reference queue in the WeakHashMap contains references to weak keys
that have been reclaimed by the GC; if so, the entries are deleted from the map.
This issue is discussed in some detail in issue 6425537 (this available openly at
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6425537;
it appears to have been filed originally by Doug Lea).
The fix is simple: just add synchronization inside expungeStaleEntries
when the queue is not empty. As that is a rare occurrence, the synchronization
should be a rare occurrence, avoiding another lock contention issue.

The problem is that 6425537 is only fixed in JDK 7. Unless
the JDK team decides to fix this in a future release of JDK 6, I think the only
choice we have is to make a copy of WeakHashMap and add the fix for
code that is not using JDK 7. I plan to do this in the ORB, but I don't
know if GF 3.1 has any places that need this particular fix as well.

By the way, another way around the problem is to create the equivalent of a
ConcurrentWeakHashMap, but that is a rather tricky implementation to get
perfectly correct. The Google collections classes have a builder class that
can create such a collection, but that is not available in GlassFish.

Ken.