Proxy.isProxyClass scalability

Peter Levart peter.levart at gmail.com
Mon Jan 28 16:13:11 UTC 2013


Hi Alan,

I prepared the variant with lazy initialization of ConcurrentHashMaps 
per ClassLoader and performance measurements show no differences. So 
here's this variant:

* http://dl.dropbox.com/u/101777488/jdk8-tl/proxy/webrev.03/index.html

I also checked the ClassLoader layout and as it happens the additional 
pointer slot increases the ClassLoader object size by 8 bytes in both 
addressing modes: 32bit and 64bit. But that's a small overhead compared 
to for example the deep-size of AppClassLoader at the beginning of the 
main method: 14648 bytes (32bit) / 22432 bytes (64bit).

Regards, Peter

On 01/28/2013 01:57 PM, Peter Levart wrote:
> 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