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