一、什么是jvm?
1.1对比物理机
物理机:
jvm:
Java虚拟机与物理机
- Class文件类比输入设备
- CPU指令集类比输出设备
- JVM类比存储器、控制器、运算器等
1.2 JVM products
有哪些版本?
- Oracle:HotSpot、JRockit
- IBM:J9 VM
- Ali:TaobaoVM
- Zual:Zing
最常用的目前是HotSpot,可以通过java -version命令查看
1.3 JDK JRE JVM 三者之间的关系
官网: https://docs.oracle.com/javase/8/docs/index.html
jre 包含了jvm,jdk 包含jdk
1.4 结合JDK看JVM
- 能够把Class文件翻译成不同平台的CPU指令集
- 也是Write Once Run Anywhere的保证
1.5 HotSpot JVM Architecture
https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html
二、 class file
Java源码文件、Kotlin源码文件、Groovy源码文件等都可以编译成class文件执行。
2.1 java 测试类
public class User {
private Integer age;
private String name = "Jack";
private Double salary = 100.0;
private static String address;
public void say() {
System.out.println("Jack Say...");
}
public static Integer calc(Integer op1, Integer op2) {
op1 = 3;
Integer result = op1 + op2;
return result;
}
}
2.2 编译过程
javac User.java->User.class
2.3Class 文件格式
官方文档
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html
class 文件是一个16进制的文件
IDEA插件(BindED)——查看class文件的十六进制
文件的格式
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
2.4 文件的解析
user.class对应的16进制文件
对比类文件的格式,和上面16进制数据进行解析
-
cafebabe
u4 magic 对应的就是16进制中的 cafebabe ,cafebabe 代表的是一个class类文件的表示,只要是一个class文件就一定是 cafebabe 开头 -
0000+0034:minor_version+major_version
16进制的34等于10进制的52,表示JDK的版本为8 -
0043:constant_pool_count
16进制的43等于10进制的67,表示常量池中常量的数量是66 -
cp_info:constant_pool[constant_pool_count-1]
字面量:文本字符串,final修饰的常量等
符号引用:类和接口的全限定名、字段名称和描述符、方法名称和描述符 -
The constant pool
官网: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4
常量的基本结构
cp_info {
2u1 tag;
u1 info[];
}
2.5 反汇编
javap ‐v ‐c ‐p User.class > User.txt 进行反编译,查看字节码信息和指令等信息
JVM相对class文件来说可以理解为是操作系统;class文件相对JVM来说可以理解为是汇编语言或者机 器语言。
3.类加载机制
官网:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html
3.1 加载
3.3.1 4种ClassLoader
3.1.2 双亲委派机制
try {
if (parent != null) {
// 先找父加载器
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
- 检查某个类是否已经加载
自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个Classloader已加载,就视为已加载此类,保证此类只所有ClassLoader加载一次。 - 加载的顺序
自顶向下,也就是由上层来逐层尝试加载此类。
3.1.3 java代码
public class Demo {
public static void main(String[] args) {
// App ClassLoader
System.out.println(new Demo().getClass().getClassLoader());
// Ext ClassLoader
System.out.println(new Demo().getClass().getClassLoader().getParent());
// Bootstrap ClassLoader
System.out.println(new Demo().getClass().getClassLoader().getParent().getParent());
}
}
结果
bootstrap classloader 是c语言实现的打印不出来
3.1.4 破坏双亲委派
- tomcat
(2)SPI机制
(3)OSGi
3.2Linking
3.2.1 Verification
保证被加载类的正确性
3.2.2 Preparation
为类的静态变量分配内存,并将其初始化为默认值
3.2.3 Resolution
把类中的符号引用转换为直接引用
3.3 Initialization
对类的静态变量,静态代码块执行初始化操作
04 运行时数据区
官网:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5
4.1Method Area(方法区)
JVM运行时数据区是一种规范,真正的实现在JDK 8中就是Metaspace,在JDK6或7中就是Perm Space方法区是各个线程共享的内存区域,在虚拟机启动时创建,虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却又一个别名叫做Non-Heap(非堆),目的是与Java堆区分开来。用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。当方法区无法 满足内存分配需求时,将抛出OutOfMemoryError异常。
4.1.1常量池和运行时常量池
The Constant Pool:https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-
4.4
The Run-time Constant Pool:https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-
2.html#jvms-2.5.5
4.1.2 String常量到底存在哪
public class SCPDemo {
public static void main(String[] args) {
String str1 = "jack"; // 这个常量一定会放到字符串常量池中
String str2 = "jack";
String str3=new String("Jack");
String str4=str3.intern(); // 找字符串常量池中是否有该常量,如果有就直接返回,如果没有再创建
// equals 只会比较值 == 会比较地址
System.out.println(str1.equals(str2)); // true
System.out.println(str1==str2); // true
System.out.println(str1.equals(str3)); // true
System.out.println(str1==str3); // false
4.2 Heap(堆)
- Java堆是Java虚拟机所管理内存中最大的一块,在虚拟机启动时创建,被所有线程共享
- Java对象实例以及数组都在堆上分配
- 堆内存空间不足时,也会抛出OOM
4.2.1 Java对象内存布局
一个Java对象在内存中包括3个部分:对象头、实例数据和对齐填充
4.2.2 方法区引用指向堆
4.2.3 堆指向方法区
4.3 Java Virtual Machine Stacks(Java虚拟机栈)
(1)虚拟机栈是一个线程执行的区域,保存着一个线程中方法的调用状态。换句话说,一个Java线程的运行状态, 由一个虚拟机栈来保存,所以虚拟机栈肯定是线程私有的,独有的,随着线程的创建而创建。
(2)每一个被线程执行的方法,为该栈中的栈帧,即每个方法对应一个栈帧。调用一个方法,就会向栈中压入一个 栈帧;一个方法调用完成,就会把该栈帧从栈中弹出。
4.3.1 代码
1 void a(){
2 b();
3 }
4 void b(){
5 c();
6 }
4.3.2 压栈出栈
4.3.3 Frame(栈帧)
1 public static java.lang.Integer calc(java.lang.Integer, java.lang.Integer);
2 descriptor: (Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;
3 flags: ACC_PUBLIC, ACC_STATIC
4 Code:
5 stack=2, locals=3, args_size=2
6 0: iconst_3 // 将int类型常量3压入[操作数栈]
7 1: invokestatic #11 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
8 4: astore_0 // 将int类型值存入[局部变量0]
9 5: aload_0 // 从[局部变量0]中装载int类型值入栈
10 6: invokevirtual #12 // Method java/lang/Integer.intValue:()I
11 9: aload_1 // 从[局部变量1]中装载int类型值入栈
12 10: invokevirtual #12 // Method java/lang/Integer.intValue:()I
13 13: iadd // 将栈顶元素弹出栈,执行int类型的加法,结果入栈
14 14: invokestatic #11 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
15 17: astore_2 // 将栈顶int类型值保存到[局部变量2]中
16 18: aload_2 // 从[局部变量2]中装载int类型值入栈
17 19: areturn // 从方法中返回int类型的数据
18 LineNumberTable:
19 line 11: 0
20 line 12: 5
21 line 13: 18
4.3.5 index为0还是1
对于Java虚拟机栈中的Local Variables,到底是从0开始还是1开始,要看当前方法是static还是实例方法。
如果是static 方法,局部变量表从0开始,实例方法,是从1开始,0留给this关键字
4.3.6栈引用指向堆
4.4 Native Method Stacks(本地方法栈)
4.5 The pc Register (程序计数器)
如果线程正在执行Java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址。如果正在执行的 是Native方法,则这个计数器为空。