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@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