Proxy.isProxyClass scalability

Peter Levart peter.levart at gmail.com
Mon Jan 28 12:57:44 UTC 2013


On 01/28/2013 12:49 PM, Alan Bateman wrote:
> On 25/01/2013 17:55, Peter Levart wrote:
>>
>> :
>>
>> The solution is actually very simple. I just want to validate my 
>> reasoning before jumping to implement it:
>>
>> - for solving scalability of getProxyClass cache, a field with a 
>> reference to ConcurrentHashMap<List<String>, Class<? extends Proxy>> 
>> is added to j.l.ClassLoader
>> - for solving scalability of isProxyClass, a field with a reference 
>> to ConcurrentHashMap<Class<? extends Proxy>, Boolean> is added to 
>> j.l.ClassLoader
> I haven't had time to look very closely as your more recent changes 
> (you are clearly doing very good work here). The only thing I wonder 
> if whether it would be possible to avoid adding to ClassLoader. I 
> can't say what percentage of frameworks and applications use proxies 
> but it would be nice if the impact on applications that don't use 
> proxies is zero.
Hi Alan,

Hm, well. Any application that uses run-time annotations, is implicitly 
using Proxies. But I agree that there are applications that don't use 
either. Such applications usually don't use many ClassLoaders either. 
Applications that use many ClassLoaders are typically application 
servers or applications written for modular systems (such as OSGI or 
NetBeans) and all those applications are also full of runtime 
annotations nowadays. So a typical application that does not use neither 
Proxies nor runtime annotations is composed of bootstrap classloader, 
AppClassLoader and ExtClassLoader. The ConcurrentHashMap for the 
bootstrap classloader is hosted by j.l.r.Proxy class and is only 
initialized when the j.l.r.Proxy class is initialized - so in this case 
never. The overhead for such applications is therefore an empty 
ConcurrentHashMap instance plus the overhead for a pointer slot in the 
ClassLoader object multiplied by the number of ClassLoaders (typically 
2). An empty ConcurrentHashMap in JDK8 is only pre-allocating a single 
internal Segment:

java.util.concurrent.ConcurrentHashMap at 642b6fc7(48 bytes) {
   keySet: null
   values: null
   hashSeed: int
   segmentMask: int
   segmentShift: int
   segments: 
java.util.concurrent.ConcurrentHashMap$Segment[16]@8e1dfb1(80 bytes) {
java.util.concurrent.ConcurrentHashMap$Segment at 2524e205(40 bytes) {
       sync: 
java.util.concurrent.locks.ReentrantLock$NonfairSync at 17feafba(32 bytes) {
         exclusiveOwnerThread: null
         head: null
         tail: null
         state: int
       }->(32 deep bytes)
       table: 
java.util.concurrent.ConcurrentHashMap$HashEntry[2]@1c3aacb4(24 bytes) {
         null
         null
       }->(24 deep bytes)
       count: int
       modCount: int
       threshold: int
       loadFactor: float
     }->(96 deep bytes)
     null
     null
     null
     null
     null
     null
     null
     null
     null
     null
     null
     null
     null
     null
     null
   }->(176 deep bytes)
   keySet: null
   entrySet: null
   values: null
}->(224 deep bytes)

...therefore the overhead is approx. 224+4 = 228 bytes (on 32 bit 
pointer environments) per ClassLoader. In typical application (with 2 
ClassLoader objects) this amounts to approx. 456 bytes.

Is 456 bytes overhead too much?

If it is, I could do lazy initialization of per-classloader CHM 
instances, but then the fast-path would incur a little additional 
penalty (not to be taken seriously though).

Regards, Peter

P.S. I was inspecting the ClassValue internal implementation. This is a 
very nice piece of Java code. Without using any Unsafe magic, it 
provides a perfect performant an scalable map of lazily initialized 
independent data structures associated with Class instances. There's 
nothing special about ClassValue/ClassValueMap that would tie it to 
Class instances. In fact I think the ClassValueMap could be made generic 
so it could be reused for implementing a ClasLoaderValue, for example. 
This would provide a more performant and scalable alternative to using 
WeakHashMap<ClassLoader, ...> wrapped by synchronized wrappers for other 
uses.
If anything like that happens in the future, the proposed patch can be 
quickly adapted to using that infrastructure instead of a direct 
reference in the ClassLoader.

>
> -Alan




More information about the core-libs-dev mailing list