Debugging (Failed) Instrumentation through sun.misc.Unsafe.defineAnonymousClass

defineAnonymousClass is a super handy method of the notorious sun.misc.Unsafe “escape hatch” in the JVM. defineAnonymousClass is used as a fast-path to define specialized anonymous classes which are not rigorously verified, are not loaded into a class loader (e.g. they exist only so long as you hold a reference to one, perhaps through an instance of an object of that type), and can have their constant pools lazily patched at load-time. For classes that do not need to be accessible from anywhere other than the class that defines them, defineAnonymousClass is a much faster way to load in a class. defineAnonymousClass is used under-the-hood to implement Lambdas, and we aggressively used it to implement CROCHET.

One fun side-effect of defineAnonymousClass’ performance tweaks is that when you break verification of a class loaded through defineAnonymosuClass (for instance, if you are instrumenting classes with a JavaAgent, perhaps with ASM), you get a totally useless error message:

Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
	at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
	at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
	at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
	at org.apache.lucene.index.IndexWriter.<init>(IndexWriter.java:878)
	at org.apache.lucene.demo.IndexFiles.main(IndexFiles.java:112)
Caused by: java.lang.NoClassDefFoundError: org/apache/lucene/index/IndexWriter$$Lambda$5
	at sun.misc.Unsafe.defineAnonymousClass(Native Method)
	at sun.misc.Unsafe.defineAnonymousClass$$PHOSPHORTAGGED(Unsafe.java)
	at java.lang.invoke.InnerClassLambdaMetafactory.spinInnerClass(InnerClassLambdaMetafactory.java:326)
	at java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:194)
	at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:304)
	at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
	... 4 more
Caused by: java.lang.ClassNotFoundException: org.apache.lucene.index.IndexWriter$$Lambda$5
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass$$PHOSPHORTAGGED(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass$$PHOSPHORTAGGED(Launcher.java:335)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 10 more

This is not helpful. If you use a fastDebug build of OpenJDK, then you can use the flags “-XX:+UnlockDiagnosticVMOptions -XX:+VerboseVerification”, which will produce a lot of output, ending up with something like:

Verifying method org.apache.lucene.index.IndexWriter$$Lambda$5/98004513.getAsLong$$PHOSPHORTAGGED(Ledu/columbia/cs/psl/phosphor/struct/TaintedLongWithIntTag;)Ledu/columbia/cs/psl/phosphor/struct/TaintedLongWithIntTag;
StackMapTable: frame_count = 0
table = { 
 }
Verification for org.apache.lucene.index.IndexWriter$$Lambda$5/98004513 has exception pending java.lang.NoClassDefFoundError 

Which can be enough to point you to the method that’s failing verification to help you figure out what the cause of the error is (in this case, it’s that my instrumentation is adding a reference to a class not loaded in a class loader that is within the scope of the anonymous class being defined).

Note also that due to a bug in OpenJDK versions 7 and 8, it is not possible to retransform lambdas using a java agent (thanks for the pointer, Katie!).