logo头像

Always believe youself.

JVM-4

类加载与字节码技术

  • 类文件结构
  • 字节码指令:每一个指令字节码都会对应一个操作
  • 编译期处理
  • 类加载阶段
  • 类加载器
  • 运行期优化

image.png

整个流程:

java source code -> 编译器(编译成)-> 字节码 -> 经过类加载器 -> 加载到虚拟机中 -> 通过执行引擎 -> 解释器 运行(用到JVM的各种内存结构)

字节码

字节码 (有JVM规范)有 头 + 16 进制码。 可以用 16 进制转10进制,然后去常量池中进行找。

javap 可以对class 文件进行反编译。 javap -v HelloWorld.class

反编译后的结果会比 二进制字节码更容易理解。

image.png

图解字节码

image.png

练习:

image.png

a = 11  b = 34

image.png

a++ 先load,在自增 (先iload再iinc)
++a 先自增,再load (先iinc再iload)

指令顺序:

  1. bipush: 先把 10 放入 操作数 栈中
  2. istore_1 : 把10 弹出来放入 1 号槽位,给 a 赋值 -> a = 10
  3. a++ 先 load 再自增,有两条指令
    • iload_1 把 a 这个值 先 load 进来
    • iinc 两个 参数 1: 哪个槽位。2: 自增几 a= 11
  4. ++a 先自增,在load
    • iinc : a = 12
    • iload: 把 12 加入到操作数 栈
  5. 做 + ,把 那两个 (10 和 12) 都弹出了,把结果 22 加到了操作数栈
    • iadd : 10 + 12
  6. a–。先 load 再 自增
    • 把 a :12 放入操作数栈
    • 再 自减。a =11
  7. 加 a++ 这时候,放入 操作栈的 a 是 12 。所以 是 22 + 12

条件判断指令:

image.png

image.png

练习:

image.png

流程解析:

  1. x = 0 x: 局部变量中的一个槽位,值 为 0
  2. 第一次循环:

    • x++

      iload_x: 先把 x 即 0 读入到操作数栈 x 还是 0
      iinc : x++ : x = 1 这是 x 在槽位中,值(局部变量) 为 1

    • x = x++ : 把操作数栈中 的 0 拿出来 覆盖了 本地变量中的 x 。所以 x 还是 0

  3. 循环 n 次 x 还是 0

构造方法

  • <cinit()V>
    image.png

    编译器会按从上至下的顺序,收集所有 static 静态代码块,和静态成员赋值的代码,合并为一个特殊的方法<cinit>()V
    i 被 复制了 3 次, i 最终 为 30

  • <init()V>

    编译器会按从上至下的顺序,收集所有代码块 和成员变量赋值的代码块,会形成新的构成方法,但原始构造方法内的代码块总是在最后。

多态的原理

  • vtable

image.png

异常的处理

单个 catch:

字节码中有 Exception table, 即 出现异常时操作,有 from to (监测的范围) target type 这些参数。这个这些行内 出现异常,先进行类型匹配,如果发生的异常和 声明的异常(包含子类) 是否一直,一直进入 target 行。

image.png

多个 catch:

会有多个 goto。有异常出现时,之后进入到一个。会有槽位的复用,用一个槽位进行存储。

image.png

finally:

  • 每个 代码块后都会加上 finally 的代码。finally 的代码 被复制了三次放入到 try ,catch, 捕获不到的异常代码的后面。
  • 如果捕获不到声明的异常时,也会运行 finally 然后在 throw 抛出异常。

finally 练习题:

image.png

image.png

  • 先把返回的结果暂存,等finally 内容执行完在把暂存结果返回的。
  • 如果finally 有修改 return 的变量, 其实返回的还是未修改前的值(暂存的值)。

synchronized 底层原理

加锁操作,解锁 成对出现。

image.png

new 对象  会把对象的地址放入到操作数栈
dup 把对象的引用 复制了一份, 为了用两次 1) 调用构造方法 2)第二个引用复制到第一个槽位 lock,lock 局部变量引用。


monitorenter : 加锁
monitorexit : 解锁