目录
- 1 javap工具
- 2 运行流程
- 3 分析i++与++i
- 4 构造方法原理
- 4.1 cinit
- 4.2 init
- 5 方法调用
- 6 多态的原理
- 7 异常处理
1 javap工具
Oracle提供了javap工具来反编译class文件:javap -v Main.class
会显示反编译之后的class文件,主要是常量池和方法区的理解;
2 运行流程
- 首先把class的常量池信息,载入到JVM内存的方法区的运行时常量池;
- class方法的字节码,会存放到方法区;
- 启动主线程,分配栈内存,压入main()方法栈帧到栈,栈帧又有局部变量表(槽位)和最大操作数栈(已经在编译时确定了是已知的),程序计数器指向方法区的main的第一条语句由执行引擎执行;
- 执行完毕之后,弹出栈帧,结束;
3 分析i++与++i
- i++是先iload再执行iinc;
- ++i是先iinc再iload;
iinc直接在局部变量表槽位上自加,而iload是会加载到操作数栈空间进行运算;
例如:a = a++
;
先把a的值取到操作数栈,在自己的局部变量表槽上执行自加操作,然后赋值操作是把操作数栈的值又存回局部变量表槽,所以a的值不变;
4 构造方法原理
4.1 cinit
整个类初始化方法,编译器会按从上至下的顺序(先赋值后申明也可以),收集所有的静态代码块和静态成员变量赋值的代码,合并为一个特殊的方法<cinit>()v;
4.2 init
对象初始化方法,编译器会按照从上到下的顺序(先赋值后申明也可以),收集所有的{}
代码块和成员变量赋值的代码,形成新的构造方法,但是原始的构造方法代码总是在最后;
5 方法调用
- 调用构造方法指令:
invokespecial
- 调用私有方法指令:
invokespecial
- 调用final方法指令:
invokespecial
- 普通public方法指令:
invokevirtual
- 类调用静态方法指令:
invokestatic
- 对象调用静态方法指令:
invokestatic
其中invokespecial
、invokestatic
在编译期间就能确定调用是哪个方法,但是invokevirtual
不能确定(可能子类的可能父类的,多态);
6 多态的原理
当执行invokevirtual
指令时:
- 先通过栈帧中的对象引用找到对象;
- 分析对象头,找到对象的实际class(因为可以用父类引用引用子类对象);
- class结构中有vtable,它在类加载的链接阶段就已经根据方法的重写规则生成好了;
- 查表得到方法的具体地址;
- 执行方法的字节码;
7 异常处理
- 单catch块:会在编译时生成一个Exception Table,监测片段代码,要是发生异常,就匹配异常类型,匹配成功,执行异常处理代码;
- 多catch块:Exception Table存在多条异常条,每条都监控,槽位复用,同一时刻只有一个错误发生;
- finally块:在字节码层面会把finally代码块放到try后面和每一个catch后面,都会存在;除此之外,编译器会自己加上任意类型的error和Exception->any用来捕捉try块和catch块中还会出现的异常和错误,一式三份;
- SOS!!!finally块里面不要写return语句,会吞掉异常;
- return之前的值要暂存,当执行完善后工作后,才会真正return出去;