java.lang.VerifyError: Expecting a stackmap frame at branch target - java

Successfully compiled the project and build as well with Maven. This is my first maven project. But I have no idea why I'm getting the below error.
Deployeed the war on tomcat and hit my url and the below error shown in my browser.
java.lang.VerifyError: Expecting a stackmap frame at branch target 72
Exception Details:
Location:
com/ebetinc/frontend/presentation/components/Login.isToteAvailable(Ljava/lang/String;Lcom/ebetinc/frontend/dao/DatabaseDao;)Z #46: lookupswitch
Reason:
Expected stackmap frame at this location.
Bytecode:
0000000: 043d 2bb9 03a4 0100 4e2a c601 1c13 03a6
0000010: 2ab8 03aa 9900 0803 3da7 010d 2db8 03ad
0000020: 9900 692a 3a04 0236 0519 04b6 03b1 ab00
0000030: 0000 003a 0000 0002 0000 0626 0000 002c
0000040: 0000 0644 0000 001a 0019 0413 03b3 b603
0000050: b599 0017 0336 05a7 0011 1904 1303 b7b6
0000060: 03b5 9900 0604 3605 1505 ab00 0000 001c
0000070: 0000 0002 0000 0000 0000 001a 0000 0001
0000080: 0000 001a 033d a700 a02d b803 ba99 0099
0000090: 2a3a 0402 3605 1904 b603 b1ab 0000 006a
00000a0: 0000 0004 0000 af34 0000 0029 0000 af4c
00000b0: 0000 003a 0000 af4d 0000 004b 0015 51cb
00000c0: 0000 005c 1904 1303 bcb6 03b5 9900 3903
00000d0: 3605 a700 3319 0413 03be b603 b599 0028
00000e0: 0436 05a7 0022 1904 1303 c0b6 03b5 9900
00000f0: 1705 3605 a700 1119 0413 03c2 b603 b599
0000100: 0006 0636 0515 05aa 0000 001f 0000 0000
0000110: 0000 0003 0000 001d 0000 001d 0000 001d
0000120: 0000 001d 033d 1cac
Stackmap Table:
append_frame(#28,Integer,Object[#931])
append_frame(#73,Object[#200],Integer)
same_frame(#90)
same_frame(#104)
same_frame(#132)
chop_frame(#134,2)
same_frame(#137)
append_frame(#196,Object[#200],Integer)
same_frame(#213)
same_frame(#230)
same_frame(#247)
same_frame(#261)
same_frame(#292)
chop_frame(#294,2)
Can anyone throw some inputs ? Thanks for any help.
Configuration:
Java 1.7
Maven 3+

Hi this is related to some bytecode in your application. (see this note on compatibility changes for Java 7 http://www.oracle.com/technetwork/java/javase/compatibility-417013.html#incompatibilities, look there some lines below for JSR 202)
You can either
recompile all sources with JDK 7
or in case you have no access to the source
use java with paramter -XX:-UseSplitVerifier
or switch to Java 6 if you face promblems using the switch
edit Even the answer is already a bit old. Because of a current case I add some more detailed explanation.
The StackMapTable attribute in the class file was, even not documented at that time, introduced with Java 6.
Foo.java
public class Foo {
public static boolean bar(String s) {
if (s.length() == 0) {
return true;
}
return false;
}
}
$ java -version
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
$ javac Foo.java
$ javap -c -v Foo
Compiled from "Foo.java"
public class Foo extends java.lang.Object
SourceFile: "Foo.java"
minor version: 0
major version: 50
...
public static boolean bar(java.lang.String);
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokevirtual #2; //Method java/lang/String.length:()I
4: ifne 9
7: iconst_1
8: ireturn
9: iconst_0
10: ireturn
LineNumberTable:
line 3: 0
line 4: 7
line 6: 9
StackMapTable: number_of_entries = 1
frame_type = 9 /* same */
}
The class verifier did no check if the attribute was in the class or not.
Following creates the file Foo.class without the StackMatTable attribute.
FooDump.java
import org.objectweb.asm.*;
import java.io.*;
public class FooDump implements Opcodes {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("Foo.class");
fos.write(dump());
fos.close();
}
public static byte[] dump() throws Exception {
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "Foo", null, "java/lang/Object",
null);
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null,
null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V",
false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "bar",
"(Ljava/lang/String;)Z", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length", "()I",
false);
Label l0 = new Label();
mv.visitJumpInsn(IFNE, l0);
mv.visitInsn(ICONST_1);
mv.visitInsn(IRETURN);
mv.visitLabel(l0);
// this line would generate the StackMapTable attribute
// mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(ICONST_0);
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
cw.visitEnd();
return cw.toByteArray();
}
}
compile and run it
$ javac -cp asm-5.2.jar:asm-util-5.2.jar:. FooDump.java
$ java -cp asm-5.2.jar:asm-util-5.2.jar:. FooDump
check that the StackMapTable attribute is not in the file
$ javap -c -v Foo
public class Foo extends java.lang.Object
minor version: 0
major version: 50
...
public static boolean bar(java.lang.String);
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokevirtual #16; //Method java/lang/String.length:()I
4: ifne 9
7: iconst_1
8: ireturn
9: iconst_0
10: ireturn
}
FooDemo.java
public class FooDemo {
public static void main(String[] args) {
System.out.println("output: " + Foo.bar(""));
}
}
$ java -version
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
$ javac FooDemo.java
$java FooDemo
output: true
With Java 7 the class verification was changed.
For class files version 50 (Java 6) the check had a failover if the StackMapTable was missing or wrong (see: jvms-4.10.1).
Run the check with the Foo class version of Java 6.
$ java -version
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
$ javap -c -v Foo
Classfile /home/suboptimal/playground/Foo.class
Last modified Jun 9, 2017; size 232 bytes
MD5 checksum 5a7ea4a5dd2f6d1bcfddb9ffd720f9c9
public class Foo
minor version: 0
major version: 50 <-- class file Java 6
...
$ javac FooDemo.java
$ java FooDemo
output: true
This failover did not occur anymore for class files version 51 (Java 7).
To create a Foo class version of Java 7 amend the code of FooDump.java.
// cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "Foo", null, "java/lang/Object", null);
cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, "Foo", null, "java/lang/Object", null);
compile and run it
$ javac -cp asm-5.2.jar:asm-util-5.2.jar:. FooDump.java
$ java -cp asm-5.2.jar:asm-util-5.2.jar:. FooDump
check that it's a class version 51
$ java -version
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
$ javap -c -v Foo
Classfile /home/suboptimal/playground/Foo.class
Last modified Jun 9, 2017; size 232 bytes
MD5 checksum cfd57fb547ac98a1b2808549f5e9e8c1
public class Foo
minor version: 0
major version: 51 <-- class file Java 7
...
$ javac FooDemo.java
$ java FooDemo
Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target 9 in method Foo.bar(Ljava/lang/String;)Z at offset 4
In Java 7 the type check for the StackMapTable attribute can be disabled to step back to the Java 6 failover mechanism using option UseSplitVerifier.
$ java -version
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
$ java -XX:-UseSplitVerifier FooDemo
output: true
In Java 8 the verification of the StackMapTable attribute became mandatory and the option UseSplitVerifier was removed.
$ java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
$ javap -c -v Foo
Classfile /home/suboptimal/playground/Foo.class
Last modified Jun 9, 2017; size 232 bytes
MD5 checksum cfd57fb547ac98a1b2808549f5e9e8c1
public class Foo
minor version: 0
major version: 51 <-- class file Java 7
...
$ javac FooDemo.java
$ java FooDemo
Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target 9
$ java -XX:-UseSplitVerifier FooDemo
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option UseSplitVerifier; support was removed in 8.0
Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target 9
note To use always the initial version of Java 6/7/8 was done by intention to show that the behaviour was there from the beginning.
You might find some suggestions to get it running with Java 8 ...
$ java -noverify FooDemo
output: true
$ java -Xverify:none FooDemo
output: true
note This disables the bytecode verifier. Keep in mind to never disable bytecode verification in a production system.

I've had the same problem running a Java 1.7 Web Application on a Java 1.7 Weblogic 12C server, while trying to deploy the error occurs:
java.lang.VerifyError: Expecting a stackmap frame at branch target 15
Exception Details: Location: aClassPathWithClassName.$javassist_write_aSpecificField(Ljava/lang/Long;)V
#6: ifnonnull Reason: Expected stackmap frame at this location.
Bytecode: 0000000: 2ab9 00cc 0100 c700 092a 2bb5 00ce b12a 0000010: 59b9 00d0 0100 2a12 d12a b400 ce2b
b900 0000020: d505 00c0 0081 b500 ceb1
From all classes within the project it happened only with the class being instrumented, aClassPathWithClassName (in the error output above).
My local solution:
Locate javassist lib being used by the application on the POM and update it. Here it was 3.10.0.GA, changed to 3.24.1-GA.

Related

Using cannonical constructor of local record (preview-feature) in Eclipse causes verify error

I am Trying to use records preview-feature with cannonical constructors in Eclipse .
public class Example {
public static void main(String[] args) {
record Range() {
Range {
}
}
new Range();
}
}
However, at run time a verify error is caused.
Exception in thread "main" java.lang.VerifyError: Constructor must
call super() or this() before return Exception Details: Location:
Example$1Range.(II)V #36: return Reason:
Error exists in the bytecode Bytecode:
0000000: 1b1c a400 22bb 000b 5912 0d05 bd00 0f59
0000010: 031b b800 1153 5904 1cb8 0011 53b8 0017
0000020: b700 1dbf b1 Stackmap Table:
same_frame(#36)
at Example.main(Example.java:10)
This happens both in eclipse 2020-06 with JDK 14 and eclipse 2020-19 with JDK 15.0.1 and eclipse 15 support.
There is no problem running it from the command line with bin\java and no error is thrown then.
Is this working for anyone else on their eclipse?
Are there any workarounds?

java.lang.VerifyError: Stack map does not match the one at exception handler

Faced this java.lang.VerifyError with code snippet as below during JVM loading bytecode.
try{
-----
} catch (NumberFormatException|CalculationException e) {
}
Here CalculationException is custom exception which extends java.lang.RuntimeException, while NumberFormatException is standard Java RuntimeException.
While the code compile and run fine locally windows machine.
It fails with VerifyError on one of the QA/prod/Dev unix node, and works fine on other unix node. While both unix nodes have the same config (using RedHat 6.2 and 1.8 jdk and same version jar files) also compared the bytecode generated on both nodes by javap -c and found this same.
I found two ways to resolve this on erroneous node also.
1) As this error is coming at byte-code verification step, tried by disabling the bytecode verification on dev unix box as -Xverify:none (Also tried -XX:-UseSplitVerifier but dint work, as i think its disabled from jdk 8)
However as we shall not disable bytecode verification in prod, have been looking for some other workaround.
2) Another workaround is by using the parent exception: RuntimeException in catch block instead of combining two exception.
What I am not able to understand if Java does have issue with this way of catching, why compiler dint complained it and why it works on one machine and not on other which have same config.
Also the error reason is not making sense which says: CalculationException (current frame, stack[0]) is not assignable to 'java/lang/RuntimeException
While its actually assignable as tested by
if (RuntimeException.class.isAssignableFrom(CalculationException.class)){
System.out.println("Assisgnable");
}
Full Exception Details:
Location:
com/markit/valuations/marketdata/snapper/domain/credit/BeanWrapperBuilder_CDXOCompositeVolSurface.getSpreadVol(Lcom/markit/valuations/dates/ImmutableDate;Lcom/markit/valuations/marketdata/data/indexeddata/IndexedData;DLcom/markit/valuations/dates/ImmutableDate;Lcom/markit/valuations/dates/ImmutableDate;Ljava/lang/String;Ljava/lang/String;Lcom/markit/qag/analytics/credit/indexpv/swaption/CreditIndexSwaptionCalculator;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Double; #51: astore
Reason:
Type 'com/markit/valuations/common/CalculationException' (current frame, stack[0]) is not assignable to 'java/lang/RuntimeException' (stack map, stack[0])
Current Frame:
bci: #0
flags: { }
locals: { 'com/markit/valuations/marketdata/snapper/domain/credit/BeanWrapperBuilder_CDXOCompositeVolSurface', 'com/markit/valuations/dates/ImmutableDate', 'com/markit/valuations/marketdata/data/indexeddata/IndexedData', double, double_2nd, 'com/markit/valuations/dates/ImmutableDate', 'com/markit/valuations/dates/ImmutableDate', 'java/lang/String', 'java/lang/String', 'com/markit/qag/analytics/credit/indexpv/swaption/CreditIndexSwaptionCalculator', 'java/lang/String', 'java/lang/String' }
stack: { 'com/markit/valuations/common/CalculationException' }
Stackmap Frame:
bci: #51
flags: { }
locals: { 'com/markit/valuations/marketdata/snapper/domain/credit/BeanWrapperBuilder_CDXOCompositeVolSurface', 'com/markit/valuations/dates/ImmutableDate', 'com/markit/valuations/marketdata/data/indexeddata/IndexedData', double, double_2nd, 'com/markit/valuations/dates/ImmutableDate', 'com/markit/valuations/dates/ImmutableDate', 'java/lang/String', 'java/lang/String', 'com/markit/qag/analytics/credit/indexpv/swaption/CreditIndexSwaptionCalculator', 'java/lang/String', 'java/lang/String' }
stack: { 'java/lang/RuntimeException' }
Bytecode:
0x0000000: 2c19 0ab9 0015 0200 b800 cb2b 1906 ba00
0x0000010: cc00 00b6 00cd ba00 ce00 00b6 00cf 1909
0x0000020: ba00 d000 00b6 00cf 0eb8 003b b600 d1c0
0x0000030: 0091 b03a 0cbb 0048 59b7 0049 12d3 b600
0x0000040: 4b19 0ab6 004b 12d4 b600 4b2c 1254 b900
0x0000050: 1502 00b6 004b 12d5 b600 4b29 b600 4c12
0x0000060: d6b6 004b 1907 b600 4b12 d7b6 004b 1905
0x0000070: b600 5b12 d8b6 004b 1906 b600 5b12 d9b6
0x0000080: 004b 190b b600 4b12 dab6 004b 1908 b600
0x0000090: 4bb6 004d 3a0d b200 4719 0d19 0cb9 0081
0x00000a0: 0300 0eb8 003b b0
Exception Handler Table:
bci [0, 50] => handler: 51
bci [0, 50] => handler: 51
Stackmap Table:
same_locals_1_stack_item_frame(#51,Object[#535])
Cause
This happened to me when there were conflicting versions of the same library (Jar) in my dependencies. To be more specific, I was importing different versions of Jackson library v2.9.10 and v2.11.0.
Troubleshooting
Start your application in verbose mode and make java log all the classes it is loading to see where the conflicting class is coming from. This can be done by passing the flag -verbose:class
Fix
Remove conflicting versions of dependencies and make sure all related dependencies/libraries are of the same major version (at the least) and preferably the same minor version too.
Remove dependencies that transitively import conflicting version of direct dependencies.
If you are not seeing any conflicting dependencies even after using mvn dependency:tree or Maven Helper plugins, the conflicting dependency could have been imported by one of the libraries' classpath. I know this sounds weird, since only applications should have classpath, but if someone added <addClasspath>true</addClasspath> in their library pom file, it could lead to this.
The dependency crash is the cause as Aditya pointed out. If you are using SBT, here is how you can resolve it.
First, add the dependency graph to your global plugins ~/.sbt/1.0/plugins/plugins.sbt:
SBT 1.4+: add
addDependencyTreePlugin
SBT <1.3: add
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.10.0-RC1")
Then in your project run
sbt "whatDependsOn com.fasterxml.jackson.core jackson-databind"
were com.fasterxml.jackson.core is the organization and jackson-databind is the artifact name of conflicting library. Mine also was because play-json is using 2.10.4 and Confluent packages use 2.9.9. You have to find yours based on the error messages that you received.
Then from this point forward, you need to make a decision on how to resolve it. Upgrade packages, or downgrade, or exclude libraries. In my scenario, because play-json was transitive dependency from a library that was not really critical in my application, I picked the easiest path and excluded 2.10.4 from play-json library.
you need to set the following jvm args in your config:
-XX:-UseSplitVerifier

java.lang.VerifyError with inner class & lambda

The follow code compiles but causes a java.lang.VerifyError. The error occurs even if the run() method is not executed.
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class TestCase {
public static void main(String[] args) {
new TestCase().run();
}
public void run() {
class Inner {
}
Map<String, Inner> map = new HashMap<>();
Function<String, Inner> function = (name) -> {
Inner i = map.get(name);
if (i == null) {
i = new Inner();
map.put(name, i);
}
return i;
};
function.apply("test");
}
}
The Error:
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
TestCase.lambda$0(Ljava/util/Map;Ljava/lang/String;)LTestCase$1Inner; #20: invokespecial
Reason:
Type 'java/util/Map' (current frame, stack[2]) is not assignable to 'TestCase'
Current Frame:
bci: #20
flags: { }
locals: { 'java/util/Map', 'java/lang/String', 'TestCase$1Inner' }
stack: { uninitialized 15, uninitialized 15, 'java/util/Map' }
Bytecode:
0000000: 2a2b b900 2d02 00c0 0032 4d2c c700 15bb
0000010: 0032 592a b700 344d 2a2b 2cb9 0037 0300
0000020: 572c b0
Stackmap Table:
append_frame(#33,Object[#50])
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2688)
at java.lang.Class.getMethod0(Class.java:2937)
at java.lang.Class.getMethod(Class.java:1771)
at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
However, if I move the 'Inner' class to be an inner class of TestCase (instead of declared in a method), the error goes away. Or, if I use an anonymous class to define the Function, the error goes away. It seems to be an issue with a class declared in the method and the use of a lamba.
Is this a JVM bug? Or am I missing something? I am using Oracle's Java 8. The error happens both on the command line and within Eclipse 4.4.
EDIT:
I upgraded to the latest JDK:
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)
When compile via javac manually and run it works fine. If I run the class compiled by Eclipse, it doesn't. So now I suspect that the Eclipse compiler has a bug.
verify error is thrown when your compiler generates a code that is not verifiable by verifier.
when the "verifier" detects that a class file, though well formed, contains some sort of internal inconsistency or security problem. So it is clearly an issue with your eclipse compiler as you have suggested. it is unable to properly compile these constructs.

How to use -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print option with JVM HotSpot

I 'm trying to use -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod command lines as described in this post.
It seems thats it's available with open-jdk (https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly).
How can I use those options (or similar equivalents) with oracle JDK7 and the JVM HotSpot?
These instructions apply to Linux (Ubuntu 10.04.4 LTS), but should be applicable for your OS. After downloading Oracle JDK 7u3 and appropriately setting your JAVA_HOME and PATH environment variables, execute the following to check available options:
java -XX:+AggressiveOpts -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version
You should see the UnlockDiagnosticVMOptions, CompileCommand and PrintAssembly options are available. Using the CompileCommand option will also enable the PrintAssembly option. However, you will need the HotSpot disassembler plugin for PrintAssembly to work; without it, you might see something like the following:
$ java -version
java version "1.7.0_03"
Java(TM) SE Runtime Environment (build 1.7.0_03-b04)
Java HotSpot(TM) Server VM (build 22.1-b02, mixed mode)
$ java -server -XX:+UnlockDiagnosticVMOptions '-XX:CompileCommand=print,*Main.main' Main
CompilerOracle: print *Main.main
Java HotSpot(TM) Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output
Compiled method (c2) 68 1 % Main::main # 4 (49 bytes)
total in heap [0xb3a97548,0xb3a979ec] = 1188
relocation [0xb3a97610,0xb3a97624] = 20
main code [0xb3a97640,0xb3a97840] = 512
stub code [0xb3a97840,0xb3a97850] = 16
oops [0xb3a97850,0xb3a97858] = 8
scopes data [0xb3a97858,0xb3a97898] = 64
scopes pcs [0xb3a97898,0xb3a979e8] = 336
dependencies [0xb3a979e8,0xb3a979ec] = 4
Could not load hsdis-i386.so; library not loadable; PrintAssembly is disabled
OopMapSet contains 1 OopMaps
To get the HotSpot disassembler plugin, you will need to build it. Looking at the OpenJDK 7u2 source, the hsdis plugin readme says:
To use the plugin with a JVM, you need a new version that can load it.
If the product mode of your JVM does not accept -XX:+PrintAssembly,
you do not have a version that is new enough.
To build this project you [need] a copy of GNU binutils to build against.
In theory this should be buildable on Windows but getting a working
GNU build environment on Windows has proven difficult.
We have confirmed above that Oracle JDK 7u3 supports PrintAssembly. I followed the hsdis plugin readme instructions, downloaded GNU binutils 2.22, placed it in the hsdis build/binutils directory and ran make. This eventually produced the following error:
hsdis.c:32:20: error: sysdep.h: No such file or directory
To correct this, I changed hsdis.c using the following patch:
diff -r 6259c6d3bbb7 src/share/tools/hsdis/hsdis.c
--- a/src/share/tools/hsdis/hsdis.c Mon Dec 12 23:08:01 2011 -0800
+++ b/src/share/tools/hsdis/hsdis.c Thu Feb 23 09:26:37 2012 -0500
## -29,7 +29,7 ##
#include "hsdis.h"
-#include <sysdep.h>
+#include <errno.h>
#include <libiberty.h>
#include <bfd.h>
#include <dis-asm.h>
Running make was then successful. Now just copy the hsdis-i386.so plugin in the hsdis build directory to the Oracle JDK 7u3 jre/lib/i386 directory.
Now you can see the disassembled compiled code:
$ java -server -XX:+UnlockDiagnosticVMOptions '-XX:CompileCommand=print,*Main.main' Main
CompilerOracle: print *Main.main
Java HotSpot(TM) Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output
Compiled method (c2) 68 1 % Main::main # 4 (49 bytes)
total in heap [0xb3999548,0xb39999ec] = 1188
relocation [0xb3999610,0xb3999624] = 20
main code [0xb3999640,0xb3999840] = 512
stub code [0xb3999840,0xb3999850] = 16
oops [0xb3999850,0xb3999858] = 8
scopes data [0xb3999858,0xb3999898] = 64
scopes pcs [0xb3999898,0xb39999e8] = 336
dependencies [0xb39999e8,0xb39999ec] = 4
Loaded disassembler from [snip]/jdk1.7.0_03/jre/lib/i386/hsdis-i386.so
Decoding compiled method 0xb3999548:
Code:
[Disassembling for mach='i386']
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} 'main' '([Ljava/lang/String;)V' in 'Main'
0xb3999640: call 0xb6ff8510 ; {runtime_call}
0xb3999645: data32 xchg %ax,%ax
0xb3999648: mov %eax,-0x3000(%esp)
0xb399964f: push %ebp
0xb3999650: sub $0x38,%esp
0xb3999656: mov %ecx,%esi
0xb3999658: mov 0x4(%esi),%ebp
0xb399965b: mov 0x8(%esi),%edi
0xb399965e: mov (%ecx),%esi
0xb3999660: mov %ecx,(%esp)
0xb3999663: call 0xb7078cf0 ;*iload_3
[snip]
0xb399983e: hlt
0xb399983f: hlt
[Exception Handler]
[Stub Code]
0xb3999840: jmp 0xb39981e0 ; {no_reloc}
[Deopt Handler Code]
0xb3999845: push $0xb3999845 ; {section_word}
0xb399984a: jmp 0xb397e220 ; {runtime_call}
0xb399984f: .byte 0x0
OopMapSet contains 1 OopMaps
#0
OopMap{off=468}
The test class I've used is:
public class Main {
public static void main(final String[] args) {
long x = 0;
for (int i = 0; i < 1000000; i++) {
x += calculate(i);
}
System.out.println("x=" + x);
}
private static long calculate(final int i) {
return (long)i * (long)i;
}
}
in my case to see the disassembled compiled code:
$ java -XX:CompileThreshold=1 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand="compileonly pac/kage/MyClass myMethod" MyClass
in example above has is cycle: for (int i = 0; i < 1 000 000; i++) {...},
that is why in our case without 1 000 000 iterations we need -XX:CompileThreshold=1 option (by default 10 000 for -server) to see our disassembled compiled code.

Tricky try-catch java code

public class Strange1 {
public static void main(String[] args) {
try {
Missing m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
public class Strange2 {
public static void main(String[] args) {
Missing m;
try {
m = new Missing();
} catch (java.lang.NoClassDefFoundError ex) {
System.out.println("Got it!");
}
}
}
class Missing {
Missing() { }
}
If you run Strange1 and Strange2 after deleting Missing.class, Strange1 will throw NoClassDefFoundError; but Strange2 will print Got it!
Can anyone explain that? Thanks.
updated:
java bytecode for Strange1 :
0 new info.liuxuan.test.Missing [16]
3 dup
4 invokespecial info.liuxuan.test.Missing() [18]
7 astore_1 [m]
8 goto 20
11 astore_1 [ex]
12 getstatic java.lang.System.out : java.io.PrintStream [19]
15 ldc <String "Got it!"> [25]
17 invokevirtual java.io.PrintStream.println(java.lang.String) : void [27]
20 return
Exception Table:
[pc: 0, pc: 8] -> 11 when : java.lang.NoClassDefFoundError
Line numbers:
[pc: 0, line: 14]
[pc: 11, line: 15]
[pc: 12, line: 16]
[pc: 20, line: 18]
Local variable table:
[pc: 0, pc: 21] local: args index: 0 type: java.lang.String[]
[pc: 8, pc: 11] local: m index: 1 type: info.liuxuan.test.Missing
[pc: 12, pc: 20] local: ex index: 1 type: java.lang.NoClassDefFoundError
java bytecode for Strange2 :
0 new info.liuxuan.test.Missing [16]
3 dup
4 invokespecial info.liuxuan.test.Missing() [18]
7 astore_1 [m]
8 goto 20
11 astore_2 [ex]
12 getstatic java.lang.System.out : java.io.PrintStream [19]
15 ldc <String "Got it!"> [25]
17 invokevirtual java.io.PrintStream.println(java.lang.String) : void [27]
20 return
Exception Table:
[pc: 0, pc: 8] -> 11 when : java.lang.NoClassDefFoundError
Line numbers:
[pc: 0, line: 15]
[pc: 11, line: 16]
[pc: 12, line: 17]
[pc: 20, line: 19]
Local variable table:
[pc: 0, pc: 21] local: args index: 0 type: java.lang.String[]
[pc: 8, pc: 11] local: m index: 1 type: info.liuxuan.test.Missing
[pc: 12, pc: 20] local: ex index: 2 type: java.lang.NoClassDefFoundError
There is only one place is different:
11 astore_1 [ex]
and
11 astore_2 [ex]
updated again:
Everyone can try it in eclipse.
Prior to saying anything, i doub't this code won't even compile. because when compiler cannot find a class (Since its deleted). may be you are getting an error when trying to compile it using javac command. if thats the case its pretty normal and in no way its weird.
and let me add an another point.. check your imports, to contain Missing class. if it is there then remove it. and tell us whats happening.
I created two java files. Strange1.java contained classes Strange1 and Missing. Strange2.java contained Strange2 class. I removed Missing.class.
I got "Got it!" from both.
Please see the following details:
manohar#manohar-natty:~$ java -version
java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) Server VM (build 20.0-b11, mixed mode)
manohar#manohar-natty:~$ gedit Strange1.java
manohar#manohar-natty:~$ gedit Strange2.java
manohar#manohar-natty:~$ javac Strange1.java
manohar#manohar-natty:~$ javac Strange2.java
manohar#manohar-natty:~$ java Strange1
manohar#manohar-natty:~$ java Strange2
manohar#manohar-natty:~$ rm Missing.class
manohar#manohar-natty:~$ java Strange1
Got it!
manohar#manohar-natty:~$ java Strange2
Got it!
I executed it in Ubuntu 11.04 linux machine.
So it might be the java's version that you are using.
NoClassDefFoundError is thrown whenever the first reference(declaring or creating an instance) to the missing class is made. Now, throwing an error or catching it depends on whether you use try-catch block for your first reference or not.
Output
The behavior of both programs depends on the version of javac used to compile them and not the version of java used to run the compiled classes. However, it's easier to use the same javac and java versions.
We'll be using J2SE 5.0 and Java SE 6 because those are the earliest versions where the programs' behavior deviates.
With build 1.5.0_22-b03:
$ jdk1.5.0_22/bin/javac {Strange1,Strange2,Missing}.java
$ rm Missing.class
$ jdk1.5.0_22/bin/java Strange1
Exception in thread "main" java.lang.NoClassDefFoundError: Missing
$ jdk1.5.0_22/bin/java Strange2
Got it!
$ jdk1.5.0_22/bin/javap -c Strange1
Compiled from "Strange1.java"
public class Strange1 extends java.lang.Object{
public Strange1();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class Missing
3: dup
4: invokespecial #3; //Method Missing."<init>":()V
7: astore_1
8: goto 20
11: astore_1
12: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #6; //String Got it!
17: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: return
Exception table:
from to target type
0 8 11 Class java/lang/NoClassDefFoundError
}
Compiled from "Strange2.java"
public class Strange2 extends java.lang.Object{
public Strange2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class Missing
3: dup
4: invokespecial #3; //Method Missing."<init>":()V
7: astore_1
8: goto 20
11: astore_2
12: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #6; //String Got it!
17: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: return
Exception table:
from to target type
0 8 11 Class java/lang/NoClassDefFoundError
}
With build 1.6.0_45-b06:
$ jdk1.6.0_45/bin/javac {Strange1,Strange2,Missing}.java
$ rm Missing.class
$ jdk1.6.0_45/bin/java Strange1
Got it!
$ jdk1.6.0_45/bin/java Strange2
Got it!
$ jdk1.6.0_45/bin/javap -c Strange1
<Same output as the corresponding command for J2SE 5.0>
$ jdk1.6.0_45/bin/javap -c Strange2
<Same output as the corresponding command for J2SE 5.0>
Analysis
The bytecode of Strange1 and Strange2 is nearly identical, except for the mapping of the catch parameter ex to a VM local variable. Strange1 stores it in VM variable 1, i.e., 11: astore_1. Strange2 stores it in VM variable 2, i.e., 11: astore_2.
In both classes, the local variable m is stored in VM variable 1. Both versions of main also have a merge point where the flow of control from two different code paths converge. The merge point is 20: return. It can be reached either by completing the try block normally, i.e., 8: goto 20, or by completing the catch block and falling through from instruction 17.
The existence of the merge point causes an exception during the verification of class Strange1, but not class Strange2 in J2SE 5.0.
JLS, Java SE 6 Edition - Chapter 12 - Execution specifies activities that occur during execution of a program:
The Java Virtual Machine starts up by loading a specified class and then invoking the method main in this specified class. Section §12.1 outlines the loading, linking, and initialization steps involved in executing main, as an introduction to the concepts in this chapter. Further sections specify the details of loading (§12.2), linking (§12.3), and initialization (§12.4).
JLS, Java SE 6 Edition - Section 12.3 - Linking of Classes and Interfaces specifies that the first activity that is involved in linking is verification.
JVMS, Java SE 7 Edition - Section 4.10 - Verification of class Files specifies the two strategies that a VM may use for verification:
There are two strategies that Java Virtual Machine implementations may
use for verification:
Verification by type checking must be used to verify class files whose version number is greater than or equal to 50.0.
Verification by type inference must be supported by all Java Virtual Machine implementations, except those conforming to the Java
ME CLDC and Java Card profiles, in order to verify class files whose
version number is less than 50.0.
Verification on Java Virtual Machine implementations supporting the Java ME CLDC and Java Card profiles is governed by their
respective specifications.
(Verification by type checking was added to JVMS, Second Edition for Java SE 6, and JVMS, Java SE 7 Edition incorporates these changes in a more accessible way.)
Verification by type inference (for class files whose version number is less than 50.0, i.e., Java SE 6)
To merge two local variable array states, corresponding pairs of local
variables are compared. If the two types are not identical, then
unless both contain reference values, the verifier records that the
local variable contains an unusable value. If both of the pair of
local variables contain reference values, the merged state contains a
reference to an instance of the first common superclass of the two
types.
JVMS, Second Edition - Section 4.9.2 - The Bytecode Verifier
When instruction 20 is reached from instruction 8 in Strange1.main, VM variable 1 contains an instance of the class Missing. When reached from instruction 17, it contains an instance of the class NoClassDefFoundError.
Because Missing.class has been deleted, the verifier can't load it to determine the first common superclass, and throws a NoClassDefFoundError. Note that there is no stack trace printed for the uncaught exception because it's thrown during verification, before class initialization and long before main begins execution.
Verification by type checking (for class files whose version number is greater than or equal to 50.0, i.e., Java SE 6)
(I've tried my best to follow the rules as precisely as possible. However, they are complex, dense and new to me. Hence, if you spot any mistakes, please feel free to correct them. If you could summarize the rules, that'd be great too.)
Because of the StackMapTable attribute, it's not necessary for the verifier to compute the first common superclass to merge the two VM variable 1's types as in the other strategy. Also, there's no need to actually load classes Missing, NoClassDefFoundError, or any other classes other than Strange1 for verification thanks to the constant pool and how the rules work.
Hence, there are no exceptions during verification. If you modify the catch block to print out the exception's stack trace, you'll see that the exception is thrown during the execution of Strange1.main with a proper stack trace:
# Modify Strange1.main's catch block to print out the exception's stack trace
$ jdk1.6.0_45/bin/javac {Strange1,Missing}.java
$ rm Missing.class
$ jdk1.6.0_45/bin/java Strange1
java.lang.NoClassDefFoundError: Missing
at Strange1.main(Strange1.java:4)
Caused by: java.lang.ClassNotFoundException: Missing
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
... 1 more
Got it!
The StackMapTable of Strange1:
$ jdk1.6.0_45/bin/javap -verbose Strange1 | tail
line 10: 20
StackMapTable: number_of_entries = 2
frame_type = 75 /* same_locals_1_stack_item */
stack = [ class java/lang/NoClassDefFoundError ]
frame_type = 8 /* same */
}
Let's go through the rules (as the verifier) to prove that Strange1.main is type safe. Each instruction of Strange1.main is verified to be type safe and satisfies all applicable (exception) handlers:
methodWithCodeIsTypeSafe(Strange1Class, MainMethod) :-
parseCodeAttribute(Strange1Class, MainMethod, FrameSize, MaxStack,
ParsedCode, Handlers, StackMap),
mergeStackMapAndCode(StackMap, ParsedCode, MergedCode),
methodInitialStackFrame(Strange1Class, MainMethod, FrameSize, StackFrame, ReturnType),
Environment = environment(Strange1Class, MainMethod, ReturnType, MergedCode,
MaxStack, Handlers),
handlersAreLegal(Environment),
mergedCodeIsTypeSafe(Environment, MergedCode, StackFrame).
mergedCodeIsTypeSafe(Environment, [instruction(Offset, Parse) | MoreCode],
frame(Locals, OperandStack, Flags)) :-
instructionIsTypeSafe(Parse, Environment, Offset,
frame(Locals, OperandStack, Flags),
NextStackFrame, ExceptionStackFrame),
instructionSatisfiesHandlers(Environment, Offset, ExceptionStackFrame),
mergedCodeIsTypeSafe(Environment, MoreCode, NextStackFrame).
mergedCodeIsTypeSafe(Environment, [stackMap(Offset, MapFrame) | MoreCode],
afterGoto) :-
mergedCodeIsTypeSafe(Environment, MoreCode, MapFrame).
instructionSatisfiesHandlers(Environment, Offset, ExceptionStackFrame) :-
exceptionHandlers(Environment, Handlers),
sublist(isApplicableHandler(Offset), Handlers, ApplicableHandlers),
checklist(instructionSatisfiesHandler(Environment, ExceptionStackFrame),
ApplicableHandlers).
instructionSatisfiesHandler(Environment, StackFrame, Handler) :-
...
/* The stack consists of just the exception. */
StackFrame = frame(Locals, _, Flags),
ExcStackFrame = frame(Locals, [ ExceptionClass ], Flags),
operandStackHasLegalLength(Environment, ExcStackFrame),
targetIsTypeSafe(Environment, ExcStackFrame, Target).
targetIsTypeSafe(Environment, StackFrame, Target) :-
offsetStackFrame(Environment, Target, Frame),
frameIsAssignable(StackFrame, Frame).
Instructions 0-7 are type safe because instructionIsTypeSafe is true and frameIsAssignable(Environment, frame(Locals, [ NoClassDefFoundErrorClass ], Flags), frame(Locals, [ NoClassDefFoundErrorClass ], Flags)) is true for each of them thanks to the first stack map frame for the (exception) handler target at instruction 19 (= 75 - 64):
frame_type = 75 /* same_locals_1_stack_item */
stack = [ class java/lang/NoClassDefFoundError ]
Instructions 12-17 are type safe thanks to the constant pool and the corresponding rules which are not listed (because they don't make up of the StackMapTable attribute).
There are 3 instructions left to prove their type-safety:
8: goto 20 is type safe thanks to the second stack map frame for the target at instruction 20 (= 75 - 64 + 8 + 1 = 19 + 8 + 1), instructing the verifier that the stack frame there is the same as the previous stack frame at instruction 8:
frame_type = 8 /* same */
instructionIsTypeSafe(goto(Target), Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
targetIsTypeSafe(Environment, StackFrame, Target),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
11: astore_1 is type safe because the store is type safe, because it can pop a NoClassDefFoundError that is a subtype of reference off the stack (thanks to the first stack map frame again), and then legally assign that type to the local variable 1, i.e., Locals = [arrayOf(String), class(Missing, Lm)] -> NewLocals = [arrayOf(String), class(NoClassDefFoundError, Ln)].
An astore instruction with operand Index is type safe and yields an outgoing type state NextStackFrame, if a store instruction with operand Index and type reference is type safe and yields an outgoing type state NextStackFrame.
instructionIsTypeSafe(astore(Index), Environment, _Offset, StackFrame,
NextStackFrame, ExceptionStackFrame) :-
storeIsTypeSafe(Environment, Index, reference, StackFrame, NextStackFrame),
exceptionStackFrame(StackFrame, ExceptionStackFrame).
More precisely, the store is type safe if one can pop a type ActualType that "matches" Type (that is, is a subtype of Type) off the operand stack (§4.10.1.4), and then legally assign that type the local variable LIndex.
storeIsTypeSafe(_Environment, Index, Type,
frame(Locals, OperandStack, Flags),
frame(NextLocals, NextOperandStack, Flags)) :-
popMatchingType(OperandStack, Type, NextOperandStack, ActualType),
modifyLocalVariable(Index, ActualType, Locals, NextLocals).
20: return is type safe because Strange1.main declares a void return type, and the enclosing method is not an <init> method:
A return instruction is type safe if the enclosing method declares a void return type, and either:
- The enclosing method is not an <init> method, or
- this has already been completely initialized at the point where the instruction occurs.
instructionIsTypeSafe(return, Environment, _Offset, StackFrame,
afterGoto, ExceptionStackFrame) :-
thisMethodReturnType(Environment, void),
StackFrame = frame(_Locals, _OperandStack, Flags),
notMember(flagThisUninit, Flags),
exceptionStackFrame(StackFrame, ExceptionStackFrame).

Categories

Resources