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@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@2524e205(40 bytes) {
sync:
java.util.concurrent.locks.ReentrantLock$NonfairSync@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