【JVM】总结篇-字节码篇
字节码篇
Java虚拟机的生命周期
JVM的组成
Java虚拟机的体系结构
什么是Java虚拟机
虚拟机:指以软件的方式模拟具有完整硬件系统功能、运行在一个完全隔离环境中的完整计算机系统 ,是物理机的软件实现。
常用的虚拟机有VMWare,Visual Box,Java Virtual Machine(Java虚拟机,简称JVM)
class文件结构概述
知道字节码吗?字节码都有哪些?
1. 加载和存储指令(Load and Store Instructions)
这些指令用于从操作数栈或者局部变量表加载数据,或者将数据存储到局部变量表中。
加载指令:将局部变量表中的数据加载到操作数栈。
iload, aload, fload, dload, lload:将局部变量表中的基本类型(如 int、Object 等)加载到操作数栈。
iload_<index>, aload_<index>, fload_<index> 等:将局部变量表的某个索引处的数据加载到栈中(如 iload_0 加载局部变量表中索引为 0 的 int 类型数据)。
存储指令:将操作数栈中的数据存储到局部变量表中。
istore, astore, fstore, dstore, lstore:将栈顶的数据存储到局部变量表中。
istore_<index>, astore_<index>, fstore_<index> 等:将操作数栈的值存储到局部变量表的某个索引。
2. 运算指令(Arithmetic and Logical Instructions)
这些指令执行基本的数学运算或逻辑运算。
算术运算:
iadd, isub, imul, idiv, irem:对 int 类型数据进行加法、减法、乘法、除法和取余。
fadd, fsub, fmul, fdiv, frem:对 float 类型数据进行相应的运算。
dadd, dsub, dmul, ddiv, drem:对 double 类型数据进行运算。
ladd, lsub, lmul, ldiv, lrem:对 long 类型数据进行运算。
逻辑运算:
iand, ior, ixor:对 int 类型数据执行按位与、或、异或操作。
land, lor, lxor:对 long 类型数据执行按位与、或、异或操作。
3. 控制流指令(Control Flow Instructions)
这些指令用于改变程序的执行流程,包括条件跳转、循环控制等。
条件跳转指令:
ifeq, ifne, iflt, ifge, ifgt, ifle:根据栈顶数据进行条件判断(例如:如果 int 值等于 0,则跳转)。
if_icmpeq, if_icmpne, if_icmplt, if_icmpge, if_icmpgt, if_icmple:比较栈顶的两个 int 类型的值,根据比较结果跳转。
无条件跳转:
goto:无条件跳转到指定的字节码地址。
jsr, ret:用于子程序调用和返回(jsr 和 ret 已经被标记为废弃,不再常用)。
返回指令:
return:从当前方法返回。
ireturn, lreturn, freturn, dreturn, areturn:返回不同类型的方法的结果。
4. 对象操作指令(Object Manipulation Instructions)
这些指令用于对象的创建、字段访问、方法调用等操作。
对象创建:
new:创建一个新的对象(例如 new 操作符)。
newarray, anewarray, multianewarray:创建不同类型的数组。
字段操作:
getstatic, putstatic: 获取或设置类的静态字段。
getfield, putfield: 获取或设置实例字段。
方法调用:
invokevirtual, invokespecial, invokestatic, invokeinterface:调用实例方法、构造方法、静态方法或接口方法。
对象方法和字段:
instanceof:检查对象是否是某个类型的实例。
checkcast:强制类型转换。
5. 堆栈操作指令(Stack Manipulation Instructions)
这些指令用于操作操作数栈上的数据。
操作数栈数据压栈和弹栈:
pop, pop2: 弹出操作数栈顶的数据。
dup, dup2: 复制栈顶的元素。
swap: 交换操作数栈的前两个元素。
6. 类型转换指令(Type Conversion Instructions)
这些指令用于不同类型之间的转换。
类型转换:
i2b, i2c, i2s:将 int 类型转换为 byte、char、short。
l2i, f2i, d2i:将 long、float、double 转换为 int 类型。
f2l, d2l:将 float 和 double 转换为 long 类型。
f2d, i2d, l2d:将 float、int 和 long 转换为 double 类型。
7. 异常处理指令(Exception Handling Instructions)
这些指令用于异常处理的控制流。
异常跳转:
athrow:抛出异常。
异常表:
try, catch, finally 结构通过在字节码中记录异常处理表实现。
8. 常量池指令(Constant Pool Instructions)
这些指令用于访问常量池中的常量。
常量池操作:
ldc, ldc_w, ldc2_w:将常量池中的常量加载到栈中。
9. 其他指令
这些指令提供了对 Java 字节码虚拟机执行的低级控制。
监视器指令:
monitorenter, monitorexit:用于实现同步(锁定和释放锁)。
Java虚拟机中,数据类型可以分为哪几类?
int a = 1;JVM如何取得a的值
Integer x = 5;int y = 5;比较 x == y 都经过哪些步骤?
- 源代码:
Integer x = 5;
int y = 5;
boolean result = (x == y); // 这里比较 Integer 和 int
-
Java 类型和自动拆箱:
x 是一个 Integer 类型对象,而 y 是一个原始的 int 类型。
在 x == y 的比较中,x 是一个对象,y 是一个基本类型(int)。Java 会进行 自动拆箱,将 Integer 对象 x 拆箱成 int 类型,然后进行基本类型的比较。
自动拆箱的过程是通过 Integer 的 intValue() 方法完成的,因此 x.intValue() 会将 Integer 对象 x 中的值提取为 int 类型,然后与 y 进行比较。 -
编译后的字节码:
假设这段代码已经被编译成字节码,我们可以查看相应的字节码操作。
字节码指令(假设以下字节码来自于 x == y 比较的实现):
0: iconst_5 // 将字面量 5 压入操作数栈
1: invokestatic #2 // 调用 Integer.valueOf(5),返回 Integer 对象,压入操作数栈
4: astore_1 // 将 Integer 对象存储到局部变量表的索引 1(x)
5: iconst_5 // 将字面量 5 压入操作数栈
6: istore_2 // 将 5 存储到局部变量表的索引 2(y)
7: aload_1 // 将 x(Integer 对象)加载到操作数栈
8: invokevirtual #3 // 调用 Integer.intValue() 获取 x 的 int 值
11: iload_2 // 加载局部变量 y(基本类型 int)
12: if_icmpeq 17 // 比较两个 int 值是否相等,如果相等跳转到 17
15: iconst_0 // 压入 0(表示 false)
16: goto 18 // 跳转到 18 处
17: iconst_1 // 压入 1(表示 true)
18: istore_3 // 将比较结果存储到局部变量表的索引 3(result)
- 字节码分析:
iconst_5:将字面量 5 压入操作数栈。这是因为 x = 5 和 y = 5 都是常量 5。
invokestatic #2:调用 Integer.valueOf(5),将常量 5 转换成 Integer 对象,并将 Integer 对象压入操作数栈。
astore_1:将 Integer 对象(即 x)存储到局部变量表的索引 1 位置。
istore_2:将常量 5 存储到局部变量表的索引 2 位置,即 y。
aload_1:将 x(Integer 对象)从局部变量表加载到操作数栈。
invokevirtual #3:调用 Integer.intValue() 方法,将 Integer 对象 x 拆箱成基本类型 int,并将拆箱后的值压入操作数栈。
iload_2:将 y(基本类型 int)从局部变量表加载到操作数栈。
if_icmpeq 17:比较栈顶的两个 int 值是否相等。如果相等,跳转到 17,即跳过 iconst_0,表示比较结果为 true。
iconst_0:如果 x.intValue() 和 y 不相等,压入 0,表示 false。
goto 18:跳转到字节码指令 18,表示结束。
iconst_1:如果 x.intValue() 和 y 相等,压入 1,表示 true。
istore_3:将比较结果存储到局部变量表的索引 3(result)。 - 拆箱过程:
拆箱是 Java 中的自动过程,在 x == y 比较中,x 是 Integer 类型,y 是 int 类型。JVM 会自动调用 Integer 对象的 intValue() 方法,提取 Integer 中的 int 值,然后将其与 y 进行比较。
x.intValue() 等价于 x 的 int 值(即 5)。
x == y 等价于 x.intValue() == y,即 5 == 5。 - 结果:
如果 x 和 y 的值相等,x.intValue() == y 会返回 true,字节码会执行 iconst_1,最终将 true 存储在 result 中。
如果 x 和 y 的值不相等,字节码会执行 iconst_0,最终将 false 存储在 result 中。 - 总结:
在字节码层面,Integer 对象会通过 intValue() 方法拆箱为 int,然后与 int 类型的变量 y 进行比较。
字节码通过 invokevirtual 调用 Integer.intValue() 方法来实现拆箱。
比较操作会根据值的相等性进行跳转,最终将比较结果(true 或 false)存储到局部变量表中。
JVM 处理这类操作时,通过类型转换和方法调用来确保正确的类型比较和结果。
1.包装类的缓存
FLoat Double 无
2.复习intern()字符串 (常量池位置的迁移jdk版本)
String s=new String(“a”);
s.intern();
String s2=“a”;
System.out.println(s == s2);//false
String s3=new String(“a”)+new String(“a”);
s3.intern();
String s4=“aa”;
System.out.println(s3 == s4);;//true
3.String s=new String(“a”);几个对象? 2