Hi Alan, David,
I think I have a solution for scalability problems of
java.lang.reflect.Proxy:
* http://dl.dropbox.com/u/101777488/jdk8-tl/proxy/webrev.01/index.html
I designed it along the lines mentioned in previous mail (see below).
First I thought that both caches (a cache mapping request parameters of
Proxy.getProxyClass to a proxy Class and a cache for resolving the
Proxy.isProxyClass method) could be placed inside the j.l.ClassLoader.
And they actually could. I tried this and it worked correctly, but the
raw (single-threaded) performance of Proxy.isProxyClass was half the
performance of current JDK8 isProxyClass method that way. The main
performance hog I identified was the Class.getClassLoader() method. This
method delegates to a native JVM method after checking the callers
permissions. So the plan to keep the isProxyClass cache inside the
ClassLoader failed. But then the ClassValue solution that I proposed in
previous revision works beautifully and fast, so I kept this solution
for isProxyClass cache. The ThreadLocal trick to initialize the
ClassValue for a particular proxy class is necessary because ClassValue
does not have a put() method. It does have actually, but it's package
private and the comment suggests that it might not be there forever:
// Possible functionality for JSR 292 MR 1
/*public*/ void put(Class<?> type, T value) {
ClassValueMap map = getMap(type);
map.changeEntry(this, value);
}
I could use the JavaLangAccess to access it from Proxy code though, so
this is another refinement that is possible and would eliminate the need
for ThreadLocal trick.
The cache for getProxyClass method on the other hand is most naturally
hosted by j.l.ClassLoader and that's where I put it in the proposed
patch. Currently the cross-package access to it is implemented using
Unsafe, but the performance aspect is not so critical that It couldn't
be changed to using JavaLangAccess instead if desired.
I did some performance testing too. Here are the results taken on 3
different systems (i7 Linux PC, Raspberry Pi and Sun-Blade-T6320 Solaris
machine):
*
https://raw.github.com/plevart/jdk8-tl/proxy/test/proxy_benchmark_results.txt
These are the test sources i used:
*
https://github.com/plevart/jdk8-tl/blob/proxy/test/src/test/ProxyBenchmarkTest.java
*
https://github.com/plevart/micro-bench/blob/master/src/si/pele/microbench/TestRunner.java
Results show scalability and raw performance improvements for both
critical Proxy methods.
So what do you think?
Regards, Peter
On 01/25/2013 06:55 PM, Peter Levart wrote:
On 01/24/2013 03:34 PM, Peter Levart wrote:
On 01/24/2013 03:10 PM, Alan Bateman wrote:
On 24/01/2013 13:49, Peter Levart wrote:
Should I file a RFE first?
Sorry I don't have time at the moment to study the proposed patch
but just to mention that it has come up a few times, its just that
it never bubbled up to the top of anyone's list. Here's the bug
tracking it:
http://bugs.sun.com/view_bug.do?bug_id=7123493
-Alan.
I belive that is another bottleneck. It is mentioning the
Proxy.getProxyClass method which also uses synchronization for
maintaining a cache of proxy classes by request parameters. I could
as well try to fix this too in the same patch if there is interest.
Regards, Peter
Hi Alan, David,
I thought about the ways to fix Proxy.isProxyClass() scalability and
the Proxy.getProxyClass() scalability. While they are different
methods, each with it's own data structure, I think that both problems
can be solved with a single solution and that solution does not
involve neither adding fields to j.l.Class nor ClassValue.
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
Both maps hold strong references to Class objects, but only for the
classes that are loaded by the ClassLoader that references them. Each
ClassLoader already holds a strong reference to all the Class objects
for the classes that were loaded by it in a Vector. Holding another
reference does not present any problem, right?
I think this would be the best solution and it would solve both
scalability problems of j.l.Proxy in one go.
Am I missing something?
Regards, Peter