问题
I have this code -
try {
doSomething();
} catch (Exception e) {
e.printStackTrace();
}
How will this actually be implemented by the compiler. Where is the check for the exception actually put in the assembly code generated?
Update
I know that how the above code is translated to bytecode. The bytecode only translates the try-catch to corresponding try-handler blocks. I am interested in how it will be translated to assembly/and or handled by the jvm.
回答1:
The cost of try-catch block
Roughly speaking try
block does not add any exception checking code to the result assembly. It is basically a no-op as long as no exception is thrown. All the slow work is done by exception throwing code.
When try-catch
is JIT-compiled, an exception table is added aside from the code. It maps the address ranges where a handled exception may occur to the address of the corresponding exception handler. Note: these are not the bytecode indices, but the real memory addresses.
How the exception are thrown in HotSpot?
- Implicit exceptions:
NullPointerException
andStackOverflowError
are detected inside signal handler in response to a segmentation fault. ArrayIndexOutOfBoundsException
,ClassCastException
etc. are checked explicitly. The corresponding checks are inlined into the compiled code where the array access is done.OutOfMemoryError
and all other exceptions thrown from the native code are checked explicitly whenever a thread state transition is performed (vm->java or native->java).- All user exceptions thrown by
athrow
bytecode. In a fast path (when acatch
handler in the same frame exists), JIT compilesathrow
into a simple jump. Otherwise the deoptimization occurs and the exception handling is done inside the VM runtime.
Well, 'How the exception are caught at assembly level?'
In no way.
I mean, exceptions are generally not caught at assembly level - all the heavy stuff (stack walking, handler lookup, deoptimization, monitor unlocking etc.) is done in VM runtime, i.e. in C code.
回答2:
If I understand your question correctly, the following code
public class Example {
public static void main(String[] args) {
try {
otherMethod();
}
catch (Exception e) {}
try {
otherMethod();
someMethod();
}
catch (SQLException e) {}
catch (IOException e) {}
}
public static void someMethod() throws IOException {throw new IOException();}
public static void otherMethod() throws SQLException, IOException {}
}
produces the following (excerpt of human readable version of) byte code.
// main method
0: invokestatic #2 // Method otherMethod:()V
3: goto 7
6: astore_1
7: invokestatic #2 // Method otherMethod:()V
10: invokestatic #4 // Method someMethod:()V
13: goto 21
16: astore_1
17: goto 21
20: astore_1
21: return
Exception table:
from to target type
0 3 6 Class java/lang/Exception
7 13 16 Class java/sql/SQLException
7 13 20 Class java/io/IOException
You'll notice the Exception table
. This constructs instructs the VM that if an exception of type type
happens between the instruction from from
to to
, then it must goto
instruction (offset) target
. It also instructs it to push the Exception
reference on the stack so that its value can be copied and bound to the parameter in the catch
block.
You also have this piece relating to the throw
statement above.
// someMethod method
0: new #6 // class java/io/IOException
3: dup
4: invokespecial #7 // Method java/io/IOException."<init>":()V
7: athrow
The instruction athrow does the following
throws an error or exception (notice that the rest of the stack is cleared, leaving only a reference to the Throwable)
The JVM explains what happens
The objectref must be of type reference and must refer to an object that is an instance of class Throwable or of a subclass of Throwable. It is popped from the operand stack. The objectref is then thrown by searching the current method (§2.6) for the first exception handler that matches the class of objectref, as given by the algorithm in §2.10.
If an exception handler that matches objectref is found, it contains the location of the code intended to handle this exception. The pc register is reset to that location, the operand stack of the current frame is cleared, objectref is pushed back onto the operand stack, and execution continues.
If no matching exception handler is found in the current frame, that frame is popped. If the current frame represents an invocation of a synchronized method, the monitor entered or reentered on invocation of the method is exited as if by execution of a monitorexit instruction (§monitorexit). Finally, the frame of its invoker is reinstated, if such a frame exists, and the objectref is rethrown. If no such frame exists, the current thread exits.
So stack frames keep getting popped until one is found that can handle the thrown exception.
How will this actually be implemented by the compiler. Where is the check for the exception actually put in the assembly code generated?
The compiler generates the bytecode above. There is no check for an exception, only byte code instructions. The athrow
will instruct the VM to perform the task of what we call throwing an exception, which will result in popping the stack, searching exception tables in the current stack frame, etc.
回答3:
I don't have a clear answer for you but I'll provide you with the steps to get the assembly and you can dissect it based on your use case.
- Make sure that your method that you are interested in is compiled to assembly
- I usually use 2 for loops to ruin escape analysis and make sure my code is not marked NOP
- Make sure you are running a debug JVM build or have built the HotSpot disassembler - instructions to build on a Mac.
- Run your program with java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
- Be ready to see some optimised code.
Also there is a GUI tool for analysing and visualising the log file of the JIT compiler, it is called JITWatch.
Here is the class I whipped up to test this with, probably somewhat verbose but got myMethod and doSomething both to compile to assembly.
public class Question {
public static void main(String[] args) {
long result = 0;
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
result += myMethod();
}
}
System.out.println(result);
}
private static long myMethod() {
try {
return doSomething();
}
catch (Exception e) {
return 100;
}
}
private static long doSomething() {
if (System.currentTimeMillis() % 2 == 0)
return System.currentTimeMillis();
else
throw new RuntimeException();
}
}
回答4:
I would refer you to an answer to a stack overflow question.
For programs running on these machines, java byte code is machine language. There is no "assembly language".
来源:https://stackoverflow.com/questions/25007427/how-are-exceptions-caught-and-dealt-with-at-the-low-assembly-level