JDK-8057655 : Empty delegating URLClassLoader performance is erratic
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 9
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2014-09-05
  • Updated: 2019-05-22
  • Resolved: 2017-08-02
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 10
Related Reports
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)

    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:

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:

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:

That means we are running tens of times slower without a cleanup. Profiling 8u40-4g case yields the expected bottleneck:

We are spending >80% doing Dictionary::find().

N.B.: Increasing both StringTableSize and SymbolTable size to 1000001 does not help to mitigate this:

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
The fix for the linked issue JDK-7133093 improves performance for this case. Closing as a duplicate.

Ran the test with http://cr.openjdk.java.net/~coleenp/7133093.01/webrev/ against current jdk10/hs. http://cr.openjdk.java.net/~shade/8057655/jdk10-baseline.log http://cr.openjdk.java.net/~shade/8057655/jdk10-patched-7133093.log http://cr.openjdk.java.net/~shade/8057655/jdk10-1g-baseline.log http://cr.openjdk.java.net/~shade/8057655/jdk10-1g-patched-7133093.log Seems to help indeed! If you commit the current fix for JDK-7133093, we can close this as duplicate.

This works for me: $ wget http://cr.openjdk.java.net/~shade/8057655/classload-torture.zip $ unzip classload-torture.zip $ java -jar classload-torture/target/benchmarks.jar

I give up. How do I run classload-torture? I think this problem is fixed with the changes for: https://bugs.openjdk.java.net/browse/JDK-7133093 [~shade] how do I run this test? % ls classload-torture.iml pom.xml src/ target/

It is interesting that increasing the Symbol/StringTableSize does not help to mitigate the issue. But I agree, we should revisit this once hash tables get automatically resized.

See if this can potentially be addressed by the Dynamic Table Resize project.