Blocks :
|
|
Duplicate :
|
|
Relates :
|
|
Relates :
|
This seems to be the known issue, but I haven't found any quantifiable measures of it. It does affect classloader benchmarks in a profound ways. In short, if we write a very short classloader torture benchmark, which always delegates the actual classloading to a system classloader: (full code and executable JAR is here: http://cr.openjdk.java.net/~shade/8057655/classload-torture.zip) @Benchmark public Class<?> load() throws ClassNotFoundException { URLClassLoader loader = new URLClassLoader(new URL[0]); return Class.forName("java.util.HashMap", true, loader); } ...then we will observe a pathological behavior: the steady state performance is erratic. See e.g. out-of-box 8u40: http://cr.openjdk.java.net/~shade/8057655/8u40-oob.log Now, it is admittedly because we overload VM Dictionary much, and clean it up on full GCs. We can see that by increasing the heap size, pushing the full GC farther away: http://cr.openjdk.java.net/~shade/8057655/8u40-1g.log http://cr.openjdk.java.net/~shade/8057655/8u40-4g.log Notice how we gradually degrade, then recover after GC, then start degrading again. To further illustrate that, this is the run with GC forced after each iteration: http://cr.openjdk.java.net/~shade/8057655/8u40-4g-forcegc.log That means we are running tens of times slower without a cleanup. Profiling 8u40-4g case yields the expected bottleneck: http://cr.openjdk.java.net/~shade/8057655/output.txt We are spending >80% doing Dictionary::find(). N.B.: Increasing both StringTableSize and SymbolTable size to 1000001 does not help to mitigate this: http://cr.openjdk.java.net/~shade/8057655/8u40-4g-1000001.log Avoiding this issue can go two routes: 1) Doing more aggressive dictionary cleanup: arguably hard, since we need to identify dead classloaders, and unload classes first -- can we really do that without full GC? 2) Making Dictionary more resilient to overload: it does not seem to be connected with a dynamic resizing proposal
|