Proxy.isProxyClass scalability
Mandy Chung
mandy.chung at oracle.com
Wed Apr 10 15:33:49 UTC 2013
On 4/10/2013 5:35 AM, Peter Levart wrote:
> Hi Alan,
>
> I have prepared new webrev of the patch rebased to current tip of
> jdk8/tl repo:
>
> https://dl.dropboxusercontent.com/u/101777488/jdk8-tl/proxy/webrev.04/index.html
>
>
> I noticed there were quite a few changes to the j.l.r.Proxy in the
> meanwhile regarding new security checks. But @CallerSensitive changes
> seem not to have been pushed yet.
>
This has been waiting for the VM support which is now in jdk8/hotspot.
Once TL is synced up jdk8 master (soon be this week), I'll push the changes.
> Anyway, I have also simplified the caching logic a bit so that it's
> now easier to reason about it's correctness. I have re-run the
> performance benchmarks that are still very favourable:
>
> https://raw.github.com/plevart/jdk8-tl/proxy/test/proxy_benchmark_results.txt
>
>
> Proxy.getProxyClass(): is almost 15x faster single-threaded with same
> or even slightly better scalability
> Proxy.isProxyClass() == true: is about 9x faster for single-threaded
> execution and much more scalable
> Proxy.isProxyClass() == false: is about 1600x faster single-threaded
> and infinitely scalable
> Annotation.equals(): is 1.6x faster single-threaded and much more
> scalable
That's great improvement. I have another patch in Proxy coming out for
review soon. When I get several urgent things on my plate done, I'll
take a look at your patch and get back to you this week hopefully.
Mandy
>
> I also devised an alternative caching mechanism with scalability in
> mind which uses WeakReferences for keys (for example ClassLoader) and
> values (for example Class) that could be used in this situation in
> case adding a field to ClassLoader is not an option:
>
> https://github.com/plevart/jdk8-tl/blob/proxy/test/src/test/WeakCache.java
>
>
>
> 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