21

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.

nobalG
  • 4,544
  • 3
  • 34
  • 72
pdeva
  • 43,605
  • 46
  • 133
  • 171
  • 1
    that shows the bytecode which only translates the try-catch to corresponding try-handler blocks – pdeva Jul 29 '14 at 02:39
  • 6
    Well, for starters there is no "assembly code". Java is bytecodes, which are interpreted by the JVM. Or they are "compiled" by the JITC into machine instructions. No "assembly code" either way. – Hot Licks Jul 29 '14 at 02:40
  • 1
    And why do you think there's a "check for exception"? When an exception is thrown there's no need to "check" for it -- the JVM knows it's there. The JVM "just" has to figure out where in the code the exception occurred and see if any `try` ranges are enabled at that point, then transfer control to the `catch` entry point if a `try` range is found. – Hot Licks Jul 29 '14 at 02:43
  • @HotLicks I think there are checked exceptions because someone wanted to make the Java IO methods more painful to work with than needed :> – user2864740 Jul 29 '14 at 02:44
  • see also http://stackoverflow.com/questions/13168798/how-does-the-jvm-know-where-to-catch-an-exception-at-runtime – vandale Jul 29 '14 at 02:45
  • Checked exceptions are a compile-time/verifier-time thing. There is no actual runtime "check". – Hot Licks Jul 29 '14 at 02:46
  • @vandale that just talks about the bytecode representation of java which i already described in my comment above and not what i am interested in – pdeva Jul 29 '14 at 02:47
  • @pdeva - Look at it again. – Hot Licks Jul 29 '14 at 03:01
  • 2
    the following paper outlines some techniques in the Latte jit compiler but is from 2000: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.31.9511 – vandale Jul 29 '14 at 03:09
  • This is particularly interesting in native C++ code. In Windows it makes use of a OS feature known as "structured exception handling". – seand Jul 29 '14 at 03:30
  • @pdeva I've explained in my answer how it is handled by the JVM, which basically starts at `athrow`. Are you looking for a specific implementation? If so, which one? – Sotirios Delimanolis Jul 29 '14 at 04:26
  • @SotiriosDelimanolis, the comments by seand and vandale point to what could turn out to be a good answer for this question. I am looking for low level details, not a high level description of how exceptions work (as you have described in your answer). – pdeva Jul 29 '14 at 04:41
  • @pdeva Can you describe what you mean by _low_? The Java language only describe what must happen. A JVM implementation actually does it. Are you looking for that? There are many parts involved in _throwing an exception_. – Sotirios Delimanolis Jul 29 '14 at 04:43
  • in that case, yes the implementation. the hotspot one might be interesting to know about. – pdeva Jul 29 '14 at 04:55
  • @pdeva you might consider reposting your question using a C++ example. The Java snippet is encouraging people to think about jvm stuff but it sounds like you're more interested in native impls. – seand Jul 30 '14 at 03:43
  • 1
    looks like a good explanation: http://stackoverflow.com/questions/307610/how-do-exceptions-work-behind-the-scenes-in-c – seand Jul 30 '14 at 03:47
  • @Sotirios Well I guess the deoptimization done by the JIT and how the interpreter handles it then. Although for specific exceptions the c2 JIT iirc actually handles those more efficiently (one reason for that being that some insane benchmarks in the early days used OutOfBoundsExceptions to iterate through an array, because.. madness. – Voo Aug 01 '14 at 14:30
  • I don't see how the question can be answered as it stands. It's not that assembly and exceptions are totally unrelated, but there's no meaningful way to connect the two directly.The relation from Java to bytecode can be made, then the way bytecode is run in a specific JVM, then you'd have to get into how exceptions were implemented, if they used the language's exception features you'd could explore how language implemented it's own exceptions, which would still leave you at trying to understand how exceptions are implemented in assembly on each platform. It seems like a rabbit hole. – Selali Adobor Aug 05 '14 at 01:41

4 Answers4

14

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.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
13

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?

  1. Implicit exceptions: NullPointerException and StackOverflowError are detected inside signal handler in response to a segmentation fault.
  2. ArrayIndexOutOfBoundsException, ClassCastException etc. are checked explicitly. The corresponding checks are inlined into the compiled code where the array access is done.
  3. 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).
  4. All user exceptions thrown by athrow bytecode. In a fast path (when a catch handler in the same frame exists), JIT compiles athrow 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.

apangin
  • 92,924
  • 10
  • 193
  • 247
  • oops it seems like i gave the bounty to a different answer by mistake. this is the one i actually wanted to choose. – pdeva Aug 08 '14 at 15:16
  • 1
    how are correct stack traces built considering that the jvm is constantly inlining methods? – pdeva Aug 08 '14 at 15:17
  • 3
    @pdeva A good question. Similarly to an exception table JIT also generates a `ScopeDesc` table for each compiled method. This structure contains the debug info, it maps the address ranges from compiled code to the virtual stack frames. A compiled method may have multiple virtual stack frames (for inlined methods). They are recorded during JIT-compilation and then recovered from `ScopeDesc` to build a stack trace. – apangin Aug 08 '14 at 23:45
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

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();
  }
}
Community
  • 1
  • 1
toomasr
  • 4,731
  • 2
  • 33
  • 36
-1

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".

Community
  • 1
  • 1
emory
  • 10,725
  • 2
  • 30
  • 58
  • (1) What about all those others, overwhelmingly more common, processors? (2) Exceptions still have to be implemented (only now in hardware instead of software), and that's what OP is asking about, so you only made your job (explaining the implementation) harder by latching onto CPUs that support JVM bytecode natively. –  Aug 05 '14 at 07:21
  • You are right. My point is that the implementor is free to implement in any way they see fit. So the question is like how do vehicles travel - some use wheels, some use wings. You are free to come up with new vehicle implementations. – emory Aug 05 '14 at 22:30
  • That's technically true but misses the point. Yes, it's an implementation detail, but an interesting one that the question revolves around. –  Aug 06 '14 at 06:32