JDK-8164480 : Crash with assert(handler_address == SharedRuntime::compute_compiled_exc_handler(..) failed: Must be the same
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 7u111,8u102,9
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2016-08-19
  • Updated: 2019-02-15
  • Resolved: 2016-08-30
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8 JDK 9 Other
7u211Fixed 8u172Fixed 9 b138Fixed openjdk7uFixed
Related Reports
Duplicate :  
Relates :  
#  Internal Error (/scratch/opt/jprt/T/P1/214234.gerard/s/hotspot/src/share/vm/opto/runtime.cpp:1362), pid=27328, tid=27609
#  assert(handler_address == SharedRuntime::compute_compiled_exc_handler(nm, pc, exception, force_unwind, true)) failed: Must be the same

Current thread (0xdb84a400):  JavaThread "NetworkDatagramConsumer-1" [_thread_in_vm, id=27609, stack(0xd8ac5000,0xd8b16000)]

Stack: [0xd8ac5000,0xd8b16000],  sp=0xd8b13024,  free space=312k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [libjvm.so+0x14b82b7]  VMError::report_and_die(int, char const*, char const*, char*, Thread*, unsigned char*, void*, void*, char const*, int, unsigned int)+0x137
V  [libjvm.so+0x14b8fb0]  VMError::report_and_die(Thread*, char const*, int, char const*, char const*, char*)+0x30
V  [libjvm.so+0x935240]  report_vm_error(char const*, int, char const*, char const*, ...)+0x60
V  [libjvm.so+0x12dcec6]  OptoRuntime::handle_exception_C_helper(JavaThread*, nmethod*&)+0x10c6
V  [libjvm.so+0x12dd59e]  OptoRuntime::handle_exception_C(JavaThread*)+0x5e
v  ~ExceptionBlob
C  0xbaadbabe

The underlying problem happens when the exception is first triggered and we compute the exception handler address to be stored in the exception cache for this pc. During computation, another OOM error is triggered in Method::fast_exception_handler_bci_for() during class loading and as a result we do another lookup for an exception handler for this new exception, this time starting at the bci of the exception handler which caused the exception to be thrown (see SharedRuntime::compute_compiled_exc_handler()). We return the handler address for the recursive OOM error which differs from the handler address for the original exception and therefore shouldn't be stored in the cache: 1355 // Update the exception cache only when the unwind was not forced 1356 // and there didn't happen another exception during the computation of the 1357 // compiled exception handler. 1358 if (!force_unwind && original_exception() == exception()) { However, the "original_exception() == exception()" check is not strong enough because OOM errors are preallocated and re-used in Universe::gen_out_of_memory_error() if PreallocatedOutOfMemoryErrorCount == 0. In this case, the oop of the original exception and the exception that happened during handler address computation are equal and therefore we assume that no exception happened and cache the incorrect address. I verified this by adding an assert that is triggered if a recursive error occurred but "original_exception() == exception()" is still true. It happens if both exceptions are equal to Universe::_out_of_memory_error_java_heap: (gdb) print *exception._handle $10 = {_o = 0x81217a38} (gdb) print Universe::_out_of_memory_error_java_heap $11 = {_o = 0x81217a38} (gdb) print Universe::_preallocated_out_of_memory_error_avail_count $12 = 0 Prototype fix: http://cr.openjdk.java.net/~thartmann/8164480/webrev.00/

I was able to print the exception before and after the assert and no new exception happened in compute_compiled_exc_handler: [stress.process.err] java.lang.OutOfMemoryError [stress.process.err] {0x81000648} - klass: 'java/lang/OutOfMemoryError' [stress.process.err] - ---- fields (total size 8 words): [stress.process.err] - private transient 'backtrace' 'Ljava/lang/Object;' @8 NULL [stress.process.err] - private 'detailMessage' 'Ljava/lang/String;' @12 "Java heap space"{0x8113fd10} (8113fd10) [stress.process.err] - private 'cause' 'Ljava/lang/Throwable;' @16 NULL [stress.process.err] - private 'stackTrace' '[Ljava/lang/StackTraceElement;' @20 NULL [stress.process.err] - private strict 'suppressedExceptions' 'Ljava/util/List;' @24 NULL [stress.process.err] - private transient 'depth' 'I' @28 0

I was able to reproduce the problem with Kitchensink. We throw an exception in J 12362 C2 java.lang.invoke.BoundMethodHandle.<init>(Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;)V java.base@9-internal (32 bytes) @ 0xedfc6cfc [0xedfc54a0+0x0000185c] I added additional debug output to the assert. The problem is that the cached handler address does not match the computed address: # assert(handler_address == computed_address) failed: 0xedfc6d15 != 0xedfc6d1f -> 0xedfc6d15: jmp 0xedfc6d40 0xedfc6d17: jmp 0xedfc6d40 0xedfc6d19: mov %eax,0x10(%esp) 0xedfc6d1d: jmp 0xedfc6d23 -> 0xedfc6d1f: mov %eax,0x10(%esp) 0xedfc6d23: mov 0x70(%esp),%ecx 0xedfc6d27: call 0xe57479e0 0xedfc6d2c: mov %eax,%ecx 0xedfc6d2e: mov 0x10(%esp),%edx 0xedfc6d32: nop 0xedfc6d33: call 0xe5747ee0 0xedfc6d38: test %eax,%eax 0xedfc6d3a: je 0xedfc6d4b 0xedfc6d3c: mov %eax,%ecx 0xedfc6d3e: jmp 0xedfc6d42 0xedfc6d40: mov %eax,%ecx 0xedfc6d42: add $0x68,%esp 0xedfc6d45: pop %ebp 0xedfc6d46: jmp 0xe57fc460 0xedfc6d4b: mov $0xfffffff6,%ecx The exception is of type java/lang/OutOfMemoryError: (gdb) x/s ((oopDesc*)(*exception._handle))->_metadata->_klass->_name->_body 0xe4f25286: "java/lang/OutOfMemoryError\036" The exception cache contains handler address 0xedfc6d15 for a java/lang/OutOfMemoryError at pc = 0xedfc6cfc: (gdb) p *nm->_exception_cache $121 = {<CHeapObj<(MemoryType)4>> = {<AllocatedObj> = { _vptr.AllocatedObj = 0xf7453cf8 <vtable for ExceptionCache+8>}, <No data fields>}, _exception_type = 0xde6db100, _pc = {0xedfc6380 "\213\350\205\300\017\204\217\006", 0xedfc6cfc "\213��\275\377\377\377\211\\$\024\350t\035x\367\350\317x\307\b\353-\353+\353)\353'\211D$\020\353\004\211D$\020\213L$p\350\264\fx\367\213��T$\020\220\350\250\021x\367\205\300t\017\213\310\353\002\213��\304h]\351\025W\203\367\271\366\377\377\377\220\220\220\350(\035x\367\350\203x\307\b\364\364", <incomplete sequence \364\273>, 0xf1f1f1f1 <Address 0xf1f1f1f1 out of bounds> <repeats 14 times>}, _handler = { 0xedfc6cef "\353", <incomplete sequence \353>, 0xedfc6d15 "\353)\353'\211D$\020\353\004\211D$\020\213L$p\350\264\fx\367\213��T$\020\220\350\250\021x\367\205\300t\017\213\310\353\002\213��\304h]\351\025W\203\367\271\366\377\377\377\220\220\220\350(\035x\367\350\203x\307\b\364\364", <incomplete sequence \364\273>, 0xf1f1f1f1 <Address 0xf1f1f1f1 out of bounds> <repeats 14 times>}, _count = 2, _next = 0x0} (gdb) x/s nm->_exception_cache->_exception_type->_name->_body 0xe4f25286: "java/lang/OutOfMemoryError\036" Searching for the catch_pco in the ExceptionHandlerTable: nm + nm->_handler_table_offset = 0xedfc9b2c catch_pco = ret_pc - nm->code_begin() = 0xedfc6cfc - 0xedfc54a0 = 6236 Header: (gdb) print *(((HandlerTableEntry*)0xedfc9b2c)+34) $117 = {_bci = 3, _pco = 6236, _scope_depth = 0} The table has 3 matching entries: (gdb) print *(((HandlerTableEntry*)0xedfc9b2c)+35) $118 = {_bci = 95, _pco = 6236, _scope_depth = 0} (gdb) print *(((HandlerTableEntry*)0xedfc9b2c)+36) $119 = {_bci = -1, _pco = 6261, _scope_depth = 0} (gdb) print *(((HandlerTableEntry*)0xedfc9b2c)+37) $120 = {_bci = 148, _pco = 6271, _scope_depth = 0} bci = -1 corresponds to the cached handler address: nm->_code_begin + _pco = 0xedfc54a0 + 6261 = 0xedfc6d15 bci = 148 corresponds to the computed handler address: nm->_code_begin + _pco = 0xedfc54a0 + 6271 = 0xedfc6d1f The problem seems to be that during computation of the cached handler address, compute_compiled_exc_handler used bci==-1 for the ExceptionHandlerTable lookup whereas for verification we used bci==148. This could be due to a recursive exception happening while looking up the exception handler bci. Investigating.