Preview: JDK-8055854 Examine overhead in java.net.URLClassLoader (stack-less exceptions)
Stanimir Simeonoff
stanimir at riflexo.com
Wed Oct 15 23:49:36 UTC 2014
First, really nice tests.
As for hiding: missing the real classloader impl. might be quite a bumper
for some middleware implementations. That would make pretty hard to trace
dependency issues without explicit logging, the latter is usually available
but still. Also it'd depend if the classloaders actually use
super.findClass() or not.
IMO, the option should be switchable via some system property.
I am not sure about the need to hide the stackless c-tor as the effect can
be achieved by overriding fillInStackTrace(); w/o the extra baggage of
JavaLangAccess.
Overall very decent improvement.
Cheers
Stanimir
On Thu, Oct 16, 2014 at 1:30 AM, Peter Levart <peter.levart at gmail.com>
wrote:
> Hi,
>
> Recently a patch was applied to JDK9 for issue "JDK-8057936:
> java.net.URLClassLoader.findClass uses exceptions in control flow", which
> optimizes frequent exceptional path out of doPrivileged block in
> URLClassLoader.findClass() and replaces it with null return (which is not
> wrapped with PrivilegedActionException) and hence eliminates one exception
> construction. This was the low-hanging fruit. Here I present a
> similar-sized fruit which hangs a little higher...
>
> The most time-consuming part of exception construction is "remembering the
> stack trace" - the Throwable.fillInStackTrace() method. Do we always need
> stack-traces?
>
> ClassLoader API consists of two protected methods that are meant to be
> used for communication among ClassLoaders:
>
> protected Class<?> loadClass(String name, boolean resolve) throws
> ClassNotFoundException
>
> protected Class<?> findClass(String name) throws ClassNotFoundException
>
>
> loadClass() is meant to call findClass() and parent loader's loadClass()
> and both of them are meant to be arranged in subclass overrides which call
> super methods. ClassNotFoundException thrown in that arrangement is used
> only to signal unsuccessful loading of a particular class from one
> ClassLoader to the other or from super method to overriding method where
> stack traces are not needed. ClassLoader.loadClass(String name) public
> method is the public API for programmatic access and Class.forName() public
> API is wired through it. VM upcall also appears to be using the public
> loadClass(String) method.
>
> I searched for callers of protected loadClass(String name, boolean
> resolve) method and apart from some tests, all JDK code seems to calls this
> method either from overriding protected method of a subclass (the super
> call) or from protected loadClass(String name, boolean resolve) method of
> another ClassLoader that happens to have access to it either because the
> caller is in the same package as the callee or nested in the same enclosing
> class. The notable exceptions are:
>
> sun.applet.AppletClassLoader (a subclasss of URLClassLoader): overrides
> the method with public method (potentially accessible as public API), calls
> super and propagates unchanged CNFE, but it also overrides findClass(),
> calls super and never propagates exceptions from super (throws it's own CNF
> exceptions with full stacks).
>
> java.net.FactoryURLClassLoader (a subclass of URLClassLoader): overrides
> the method with public method (probably a mistake), calls super and
> propagates CNFE, but the class is package-private, so there's no public
> access to this method.
>
> sun.misc.Launcher.AppClassLoader (a subclass of URLClassLoader):
> overrides the method with public method(probably a mistake), calls super
> and propagates CNFE, but the class is package-private, so there's no public
> access to this method.
>
> And of course:
>
> java.lang.ClassLoader: delegates to the method from public
> loadClass(String name) method.
>
> That appears to be it.
>
> So here's a preview of a patch that uses stack-less
> ClassNotFoundException(s) for intra/inter-class-loader communication and
> replaces them with full-stack variants on the public API border:
>
> http://cr.openjdk.java.net/~plevart/jdk9-dev/ClassLoader.CNFE/webrev.01/
>
> I also prepared a benchmark which shows the improvement (results attached
> as comments):
>
> http://cr.openjdk.java.net/~plevart/jdk9-dev/ClassLoader.CNFE/CLBench.java
>
> The loadSameClassFailureand loadSameClassSuccess are throughput benchmarks
> which constantly call loadClass() on the AppClassLoader with the same
> unexistent and existent class names respectively. loadSameClassFailure
> shows improvement because CNF exception thrown from ExtClassLoader (parent
> of AppClassLoader) has no stack.
>
> The loadNewClassFailure and loadNewClassSuccess are one-shot benchmarks
> which run each shot on fresh VM instance. A shot loads 100,000 different
> classes. Both benchmarks show improvement because of
> intra/inter-class-loader communication is using stack-less CNF exceptions.
>
> I guess there exist other internal Oracle benchmarks that could be run
> with this patch to get some more evidence of possible improvement.
>
> So what is the effect on stack-traces? The following simple test:
>
> package test;
> public class Test {
> static void doIt() throws Exception {
> Class.forName("XXX");
> }
> public static void main(String[] args) throws Exception {
> doIt();
> }
> }
>
>
> Prints using original JDK code:
>
> Exception in thread "main" java.lang.ClassNotFoundException: XXX
> at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
> at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
> at java.security.AccessController.doPrivileged(Native Method)
> at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
> at java.lang.ClassLoader.loadClass(ClassLoader.java:426)
> at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:317)
> at java.lang.ClassLoader.loadClass(ClassLoader.java:359)
> at java.lang.Class.forName0(Native Method)
> at java.lang.Class.forName(Class.java:264)
> at test.Test.doIt(Test.java:11)
> at test.Test.main(Test.java:15)
>
> And using patched JDK code:
>
> Exception in thread "main" java.lang.ClassNotFoundException: XXX
> at java.lang.ClassLoader.loadClass(ClassLoader.java:366)
> at java.lang.Class.forName0(Native Method)
> at java.lang.Class.forName(Class.java:264)
> at test.Test.doIt(Test.java:11)
> at test.Test.main(Test.java:15)
>
>
> Some internal intra/inter-class-loader frames are missing, but the
> application stack-trace is visible. Is this too much hiding?
>
>
> Regards, Peter
>
>
More information about the core-libs-dev
mailing list