内部在哪个地方java 运行异常

In which place internally java run Exceptions

我想知道 JVM 或 JDK 运行 是如何出现异常的。例如,如果我们有数组

int tab[] = {1, 2, 3};

我们尝试访问

tab[10]

JVM 运行 ArrayIndexOutOfBoundsException 没问题。 tt可以看出这个异常是在JDK的哪一部分或者JVM执行的吗?我的意思是 throw new ArrayIndexOutOfBoundsException() 或类似的东西?从调用JNI调用是不可能的?

JVM 抛出 ArrayIndexOutOfBoundsException。字节码不检查数组索引:

    int tab[] = {1};
    int x = tab[2];

这两行的字节码:

    ICONST_1
    NEWARRAY T_INT
    DUP
    ICONST_0
    ICONST_1
    IASTORE
    ASTORE 2
   L3
    LINENUMBER 26 L3
    ALOAD 2
    ICONST_1
    IALOAD   <-- we read element 1 which is out of bounds
    ISTORE 3

如您所见,没有索引检查,也没有抛出异常。这意味着它是由 JVM 完成的

首先,请参阅 this answer 以简单了解异常在 HotSpot JVM 中的工作原理。

关于抛出 ArrayIndexOutOfBoundsException 的 JVM 代码,有多个地方可以看到:

  1. 翻译。 iaload 字节码在解释器中的实现(以及其他数组访问字节码)包括索引检查。如果检查失败,解释器跳转到异常抛出存根。见 templateTable_x86.cpp:

    void TemplateTable::iaload() {
      transition(itos, itos);
      // rax: index
      // rdx: array
      index_check(rdx, rax); // kills rbx                              --------------
      __ access_load_at(T_INT, IN_HEAP | IS_ARRAY, rax,                             |
                        Address(rdx, rax, Address::times_4,                         |
                                arrayOopDesc::base_offset_in_bytes(T_INT)),         |
                        noreg, noreg);                                              |
    }                                                                               |
                                                                                    |
    void TemplateTable::index_check(Register array, Register index) {             <--
      // Pop ptr into array                                                          
      __ pop_ptr(array);                                                             
      index_check_without_pop(array, index);                           --------------
    }                                                                               |
                                                                                    |
    void TemplateTable::index_check_without_pop(Register array, Register index) { <--
      // destroys rbx
      // check array
      __ null_check(array, arrayOopDesc::length_offset_in_bytes());
      // sign extend index for use by indexed load
      __ movl2ptr(index, index);
      // check index
      __ cmpl(index, Address(array, arrayOopDesc::length_offset_in_bytes()));
      if (index != rbx) {
        // ??? convention: move aberrant index into rbx for exception message
        assert(rbx != array, "different registers");
        __ movl(rbx, index);
      }
      Label skip;
      __ jccb(Assembler::below, skip);
      // Pass array to create more detailed exceptions.
      __ mov(NOT_LP64(rax) LP64_ONLY(c_rarg1), array);
      __ jump(ExternalAddress(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry));  !!!
      __ bind(skip);
    }
    
  2. JIT 编译器(C1 和 C2)。对方法进行 JIT 编译时,编译器会在生成的机器代码中包含类似的索引检查序列。有时,当编译器可以证明没有越界条件可能发生时,它会消除冗余检查。

    例如,C1 编译器首先在中间表示中发出与平台无关的范围检查,参见 c1_LIRGenerator.cpp:

    void LIRGenerator::array_range_check(LIR_Opr array, LIR_Opr index,
                                        CodeEmitInfo* null_check_info, CodeEmitInfo* range_check_info) {
      CodeStub* stub = new RangeCheckStub(range_check_info, index, array);
      if (index->is_constant()) {
        cmp_mem_int(lir_cond_belowEqual, array, arrayOopDesc::length_offset_in_bytes(),
                    index->as_jint(), null_check_info);
        __ branch(lir_cond_belowEqual, T_INT, stub); // forward branch
      } else {
        cmp_reg_mem(lir_cond_aboveEqual, index, array,
                    arrayOopDesc::length_offset_in_bytes(), T_INT, null_check_info);
        __ branch(lir_cond_aboveEqual, T_INT, stub); // forward branch
      }
    }
    

    然后在代码生成期间 RangeCheckStub 被扩展为依赖于平台的程序集,其中包括跳转到异常抛出存根,参见 c1_CodeStubs_x86.cpp:

      if (_throw_index_out_of_bounds_exception) {
        stub_id = Runtime1::throw_index_exception_id;
      } else {
        stub_id = Runtime1::throw_range_check_failed_id;
        ce->store_parameter(_array->as_pointer_register(), 1);
      }
      __ call(RuntimeAddress(Runtime1::entry_for(stub_id)));
    

    最终这导致在 JVM C++ 代码中调用 Exceptions::_throw 函数。