目录
  • 1. 背景介绍
  • 2. 原始java代码
  • 3. java代码对应的反编译字节码
  • 4. jvm实现分析
    • 4.1 获取数组长度arraylength指令核心代码分析
    • 4.2 获取数组元素iaload指令分析
    • 5. 小结一下
  • java 数组越界问题

    1. 背景介绍

    java中的数组比c语言中的数组,多了两个很重要的功能

    • 当索引越界时, 会自动抛出ArrayIndexOutOfBoundsException, 避免一错再错
    • 另一个很重要的方法是获取数组长度

    这两个功能都不是通过java代码层面实现的, 而是在jvm中通过c++来实现的. 本文就针对这连个点来一探究竟

    2. 原始java代码

    public class TestArrayIndexOutOfBoundsException {
        public static void main(String[] args) {
            int[] is = new int[2];
            int x = is[5];
            System.out.println(x);
            int len = is.length;
            System.out.println(len);
        }
    }

    3. java代码对应的反编译字节码

     0 iconst_2
     1 newarray 10 (int) // 创建长度为2的int型数组
     3 astore_1 // 将数组is存储到local stack的slot1中
     4 aload_1 // 将数组is压入操作数栈
     5 iconst_5 // 将常量5压入操作数栈
     6 iaload // 这个指令就是通过数组索引获取元素, is[5]
     7 istore_2
     8 getstatic #2 <java/lang/System.out>
    11 iload_2
    12 invokevirtual #3 <java/io/PrintStream.println>
    15 aload_1 // 将数组is压入操作数栈
    16 arraylength // 获取is数组的长度
    17 istore_3
    18 getstatic #2 <java/lang/System.out>
    21 iload_3
    22 invokevirtual #3 <java/io/PrintStream.println>
    25 return

    4. jvm实现分析

    4.1 获取数组长度arraylength指令核心代码分析

    // hotspot\src\share\vm\interpreter\bytecodeInterpreter.cpp
    void
    BytecodeInterpreter::run(interpreterState istate) {
    // 省略无关代码
     CASE(_arraylength):
     {
      // java中的对象实例, 对应的c++实例就是arrayOopDesc
         arrayOop ary = (arrayOop) STACK_OBJECT(-1);
         CHECK_NULL(ary);
         SET_STACK_INT(ary->length(), -1); // 就是通过ary->length()这个方法来获取数组长度
         UPDATE_PC_AND_CONTINUE(1);
     }
    }
    // 省略无关代码

    4.2 获取数组元素iaload指令分析

    // hotspot\src\share\vm\interpreter\bytecodeInterpreter.cpp
    void
    BytecodeInterpreter::run(interpreterState istate) {
    // 省略无关代码
     #define ARRAY_INTRO(arrayOff)                                                  \
    
           arrayOop arrObj = (arrayOop)STACK_OBJECT(arrayOff);                      \ // 从局部变量表中获取数组对象is
           jint     index  = STACK_INT(arrayOff + 1);                               \ // 从局部变量表中获取索引5
           char message[jintAsStringSize];                                          \
    
           CHECK_NULL(arrObj);                                                      \
    
           if ((uint32_t)index >= (uint32_t)arrObj->length()) {                     \ // 判断索引是否大于或等于数组长度
               sprintf(message, "%d", index);                                       \
    
               VM_JAVA_ERROR(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), \ // 如果越界, 则抛出ArrayIndexOutOfBoundsException异常
                             message);                                              \
    
           }
    // 省略无关代码
     #define ARRAY_LOADTO32(T, T2, format, stackRes, extra)                                \
    
           {                                                                               \
    
               ARRAY_INTRO(-2);                                                            \ // 获取数组所在的地址
               (void)extra;                                                                \
    
               SET_ ## stackRes(*(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)), \ // 根据数组所在地址,加上索引计算的偏移地址, 获得数组指定元素
                                -2);                                                       \
    
               UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);                                      \
    
           }
    // 省略无关代码
     CASE(_iaload): // 此处对应的就是iaload指令的具体实现
              ARRAY_LOADTO32(T_INT, jint,   "%d",   STACK_INT, 0);
    }
    // 省略无关代码

    通过上面代码的分析, 可以jvm是通过(uint32_t)index >= (uint32_t)arrObj->length()来判断数组越界

    5. 小结一下

    java中的数组和c语言数组差异很大, c语言中的数组更原始,直接和内存对应。而java中的数组类型其实是经过了arrayOopDesc的封装,因而能获得更丰富的信息,做更多语言层面的检查。

    java 数组越界问题

    Java中数组初始化和OC其实是一样的,分为动态初始化和静态初始化,

    • 动态初始化:指定长度,由系统给出初始化值
    • 静态初始化:给出初始化值,由系统给出长度

    在我们使用数组时最容易出现的就是数组越界问题,好了,下面来演示一下

    int [][] array = {{1,2,3},{1,4}};
            System.out.println(array[1][2]);

    这是一个二维数组,很明显,数组越界了,控制台中会打印如下信息:

    Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 2

    at demo.Array.main(Array.java:31)

    很准确的定位到31行

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。