共计 3008 个字符,预计需要花费 8 分钟才能阅读完成。
深刻了解 JVM – 字节码指令
前言
字节码指令的局部更多要和实战搭配学习和应用,所以这一节将会是简略概述字节码的相干指令内容,和 class 构造不同,字节码指令常见的命令是须要理解的,尽管咱们很多时候并不需要钻研底层字节码的指令,然而譬如动静语言的反对就是通过新增字节码指令实现的。
这一节内容更加倡议配合浏览字节码,依据字节码浏览来增强记忆。后续的文章将会独自开一篇讲一讲字节码的源代码解决。
概述
- 理解 jvm 字节码指令的根本特点
- 理解 jvm 的常见字节码指令(倡议在浏览字节码的时候不晓得什么意思的时候再来查)
- 局部指令的解决细节解说。
- 最初依据书中的内容整顿了一个 excel 表格,供浏览的时候进行疾速查阅
字节码指令
简介
java 的指令蕴含特定语意操作数字(操作码)并且占一个字节长度的,同时 java 的指令并不是面向寄存器架构的,而是采纳操作数栈(栈帧)的架构。因为一个字节码的长度被限度为 i 一个字节,所以意味着操作数字并不可能超过 256 条(1111 1111)。如果超过长度的下限,就须要依据位操作构建一个新的的数据结构,也能够了解为向上降级,比方 byte1 降级为 byte2 就能够如此示意:(byte << 8) | byte2
。尽管这种操作会损失肯定的性能,然而换来的是能够省略大量的填充占位符来实现对齐操作。
只有 tableswitch 和 lookupswitch 这两条指令例外,须要预留空位来进行对齐填充
反对数据类型
上面是 java 反对的数据类型表,这里能够看到对于 Byte,short,boolean 等操作在表上没有的,并不是说不反对,而是 jvm 解决的时候底层将他们对立作为 int 操作了,所以只须要依照 int 的操作了解即可。
指令介绍
因为指令的内容比拟多,这里同样依据书中给的图表作为笔记。
加载指令
加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输,对于局部变量表和操作数栈的内容在之前的文章中都是讲的非常抽象的,简略了解为虚拟机栈的栈帧根底构造中的内容,对于栈桢的具体构造会放到后续的文章解说。
运算指令
运算指令就是咱们常见的数学操作,包含加减乘除以及位移等。这里值得关注的是对于指令的操作 溢出,jvm 是如何解决的:
- 如果除数或者余数是 0,则会抛出 ArithmeticException 异样(运行时异样)
- 如果数据是整型或者浮点型,进行指令计算的时候如果产生溢出都不会呈现或者抛出异样
- 整型的溢出会呈现一个不稳固的随机数,并没有规定溢出后为哪一个数字,有可能负数也可能正数。
- 浮点操作因为存在精度,因而如果呈现非准确的后果会呈现“逐级下溢”的个性,意思就是小数点的局部会舍弃掉,并且舍入到一个不大于原数到最靠近到值。同样,因为精度的存在,无限小数局部会依据精度进行摄入,比方常见的
10/3
的操作后果。 - 浮点运算严格依照 IEEE 754 的标准。
类型转换指令
该指令用于尝试将两个类型进行互转,上面是 java 反对的宽化类型转化(小类型转大类型):
- int 转 long、float、double
- long 类型转 float 和 double
- float 转 double
窄化的范畴转化略微简单一些,他遵循如下的规定:
- 如果是整型类型,将会间接抛弃位数取得小类型的对应位数,这会导致数字可能变为一个正数(计算机依照最高位为 0 或者 1 判断数字的正负值)
-
如果是浮点型,则遵循如下的规定
- 如果浮点值是一个 NAN,那么转换之后还是 NAN
- 如果向下转型之后,如果不是无穷大,则依照 IEEE 754 的向零舍入模式取整,取得整数值 v,如果 v 在其数据范畴的容许范畴之内则不变,否则依照向下转型所能示意的最大值(或最小值)示意。
- 如果 Double 向下转型无奈被 float 浮点值,则依照 float 的正负值 0 进行示意。如果转化绝对值太大,则依据 float 的最大值示意。对于 double 类型的 NaN 值将按规定转换为 float 类型的 NaN 值
对象创立与拜访指令
这里只须要留神创立实例和创立数组应用了不同的命令即可。
操作数栈的指令
操作数栈的指令指的是堆栈之中操作的指令
控制指令
管制转移指令能够让 Java 虚拟机有条件或无条件地从指定地位指令 (而不是管制转移指令) 的下 一条指令持续执行程序,从概念模型上了解,能够认为控制指令就是在有条件或无条件地批改 PC 存放 器的值。这里须要留神的内容是 Java 辨别了援用类型和根底类型的比拟操作,并且有不同的指令进行辨别,另外,之前提到过的 boolean、byte、short 等指令是通过int 类型 进行比拟的
办法调用和返回指令(重点):
这一块是重点内容,也是有可能会被问到的点,当然具体的实操在后续的文章解说:
invokevirtual 指令: 用于调用对象的实例办法,依据对象的理论类型进行分派(虚办法分派),这也是 Java 语言中最常见的办法分派形式。invokeinterface 指令: 用于调用接口办法,它会在运行时搜寻一个实现了这个接口办法的对象,找 出适宜的办法进行调用。invokespecial 指令: 用于调用一些须要非凡解决的实例办法,包含实例初始化办法、公有办法和 父类办法。invokestatic 指令: 用于调用类静态方法(static 办法)。invokedynamic 指令: 用于在运行时动静解析出调用点限定符所援用的办法。并执行该办法。后面 四条调用指令的分派逻辑都固化在 Java 虚拟机外部,用户无奈扭转,而 invokedy namic 指令的分派逻辑 是由用户所设定的疏导办法决定的。
invokedynamic 指令是这一块内容的重点,至于办法分派的内容,同样会在后续的文章进行解说。
最初,办法的返回指令是依据数据类型确定的,比方 int、double、float 的返回类型指令是不一样的。这里也要再次阐明一下,byte、boolean、short 这些数据类型的返回值同样是看作 int 解决的。(能够看到 int 的重要性了)
异样解决指令
这里又一个考点,须要重点记忆:而在 Java 虚拟机中,解决异样 (cat ch 语句) 不是由字节码指令来实现的(很久之前已经应用 jsr 和 ret 指令来实现,当初曾经不必了),而是采纳异样表来实现。
同步指令
Java 虚拟机能够反对办法级的同步和办法外部一段指令序列的同步,同步一段指令集序列通常是由 Java 语言中的 synchronized 语句块来示意的,Java 虚拟机的指令集中 有 monitorenter 和monitorexit两条指令来反对 synchronized 关键字的语义,正确实现 synchronized 关键字 须要 Javac 编译器与 Java 虚拟机两者独特合作反对。
这里给出了具体的一段代码示例:
可查字节码指令表
这里集体依据书中的内容整顿了一份字节码指令的查阅表,用了石墨文档不便随时查阅。
地址:https://shimo.im/sheets/GyrHQ…《字节码指令》
总结
字节码指令的重点是办法的调用相干内容,在面试当中有可能会被问到 JDK 个性的是如何通过字节码实现的,同时有必要查看 java 的虚办法(就是最为常见的办法调用)是如何调用和实现的,借此也能够了解重载和重写的实现。
写在最初
这一节的内容其实并不需要背诵,了解有一个根底的印象即可,这部分也不是背的重点。
能够看到,专栏到了前面的内容少数都是从书上总结了,咱们不须要深刻底层,所以周大神的书多翻翻根本就够了,另外 JVM 更多还是要本人做笔记总结。