Proxy.isProxyClass scalability

Peter Levart peter.levart at gmail.com
Mon Apr 8 11:49:16 UTC 2013


Hi Alan,

Is this still being considered? Recently there were some changes in the 
j.l.r.Proxy (the @CallerSensitive annotation), so my final webrev of the 
patch will have to be rebased. Do you still think we should not add new 
fields to ClassLoader? I have some ideas how to do it without adding a 
field to ClassLoader but for the price of some additional space overhead 
for each Proxy class produced. On the other hand I think that in the 
future, more platform-internal data structures would want to be 
"attached" to ClassLoaders so perhaps a single ConcurrentHashMap per 
ClassLoader could be re-used for different purposes by using distinct 
(never-equal) keys for each purpose (fox example, the lambda metafactory 
currently does not do any caching for it's own spun SAM proxy classes or 
CallSite objects, but could benefit quite a bit from doing so).

Regards, Peter

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