JVM 详解
jvm 介绍
jvm 是什么?jvm 是java程序最终运行的地方,jdk默认虚拟机HotSpot,是java能成为跨平台的依仗!(后续介绍到jvm 整体结构的时候会说到是为什么)
大致简略图
类加载器子系统
加载阶段
类加载器分两种:引导类加载器与自定义类加载器。因为引导类加载器是C与C++编写的,扩展类与系统类的加载器都是间接性的继承ClassLoader类来实现的类加载器。扩展类加载器包含系统类加载器,虽然是包含但是实际通过代码 查看他们两个是 同级的。
- 引导类加载器:负责加载 本地库,包名java开头的jar 或者类
- 扩展类加载器:负责加载jdk\lib\ext 下面的包
- 系统类加载器:负责加载开发人员编辑创建的类
双亲委托机制
- 加载类的时候,首先会逐步向上找到最顶端的加载器
- 然后加载器根据自身负责加载条件是否满足,满足条件加载,不满足向下传递,让下一个类加载去加载
链接阶段
初始化阶段
赋值操作 举例子如下:
static int c = 1; //链接阶段 c = 0 ,初始化阶段 c = 1
双亲委托机制是为了防止:重复加载 和 对核心库的保护。举例子创建一个相同的java.lang.String 如果没有双清委托那么,就会错误的加载类 导致 原本正常的代码中String 的使用出现错误进而导致程序崩溃。
运行时数据区
程序计数器
所谓的程序计数器,可以理解为是一个记录下一个字节码指令的记录地址。执行器通过 程序计数器上找到的地址然后去java 栈中执行。
java 栈(简称 栈)
先看下详情的结构图(一个方法对应一个栈帧)
- 局部变量表(主要): 记录 非静态类的方法中,隐式关键字this ,形参,局部创建的变量。
- 操作数栈(主要):每次进行计算机指计算,都会先对值做一次入栈出栈的动作。
- 方法返回地址:记录每次正常结束或者异常结束之后下一个栈的地址。
- 动态链接:每次class 文件加载完成之后会有一个常量池,不同的常量对应不同的符号引用,动态链接就是这这些符号引用
- 一些附加信息:一般是针对内容的附加提示信息,供给给开发人员一定的帮助或者提示。
本地方法栈
负责管理本地方法的库与接口,这些本地方法库是c/c++实现编写的。
堆
结构图:
堆内存大致分为两部分:第一部分 新生代+ 老年代 称谓 堆、第二部分原空间。
- 堆空间负责存储示例对象以及数组(从jdk7包括7开始新增了字符串常量与静态变量)
- 元空间 方法区的具体实现 具体下面 方法中介绍。
- 新生代分为 :Eden 与 s0(Survivor)和s1(Survivor), s0与s1之间有个规则昵称 ‘from’与’to’ 谁空谁就是to ,反之为from 。
- Eden 区内部还有一种叫做TLAB 的区域,只有Eden的1%大小,作为线程私有,堆作为共享内存那么就会涉及到线程安全进而涉及到高并发性能 ,TLAB作用就是在一定程度上解决高并发性能的问题。后面会说到怎么解决
- 默认新生代:老生代 空间大小是 1:2
- 默认Eden:s0:s1 空间大小是:8:1:1
配置
-Xms 堆初始大小
-Xmx 堆最大
-XX:NewRatio=1 新生代老年代内存空间比例 默认1:2
-XX:SurvivorRatio=4 Eden与s0、s1的比例 默认8:1:1(提示需要显示配置才生效.)
方法区
本地方法区存储的是(最恰当的一种):类型信息、运行时常量池、静态变量、JIT 代码缓存、域(属性)信息、方法信息。简单理解字节码文件的内容存储到了方法区。
- 元空间 则是方法区的具体实现。
- 首先要明白方法区就要看懂 字节码文件如下:
/**下面是源码*/
public class CctvTest {
public static void main(String[] args) {
CctvTest cctvTest = new CctvTest();
int z = 19;
cctvTest.heoll("hl");
}
public void heoll(String str) {
System.out.println("欢迎光临" + str);
int i = heoll2(10);
}
public int heoll2(int i) {
int c = 1;
int b = 3;
String str = heoll3("hl");
return c + b + i;
}
public String heoll3(String hl) {
int c = 1;
int b = 3;
return hl + "";
}
}
/**下面是反编译class文件拿到的字节码文件*/
//文件信息
Classfile /F:/MyProject/学习开始/houliang/爬虫/NDA/cloud-alibaba-nda/alibaba-data-acquisition/target/test-classes/CctvTest.class
Last modified 2021-2-22; size 1252 bytes
MD5 checksum 3fdf47090d6d5e62d4c3ed083dc76af8
Compiled from "CctvTest.java"
//类信息
public class CctvTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER //修饰关键字
Constant pool: //运行时常量池 是数组结构 #1、#2可以理解为下标一样的东西,
//其实可以简单理解,今天要做晚饭 n个菜,我们就需要采购 例如盐味精 白菜 香葱、
//我们不是说 盐都会用到所以按每个菜去买一份盐,而是一份盐 每个菜都用。
//因为方法区只是存储这些信息,具体的实现是元空间所以,想说的是,这里只需要把
//盐、白菜、红酒...等等写好就好了 元空间去实现
#1 = Methodref #16.#45 // java/lang/Object."<init>":()V
#2 = Class #46 // CctvTest
#3 = Methodref #2.#45 // CctvTest."<init>":()V
#4 = String #42 // hl
#5 = Methodref #2.#47 // CctvTest.heoll:(Ljava/lang/String;)V
#6 = Fieldref #48.#49 // java/lang/System.out:Ljava/io/PrintStream;
#7 = Class #50 // java/lang/StringBuilder
#8 = Methodref #7.#45 // java/lang/StringBuilder."<init>":()V
#9 = String #51 // 欢迎光临
#10 = Methodref #7.#52 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#11 = Methodref #7.#53 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#12 = Methodref #54.#55 // java/io/PrintStream.println:(Ljava/lang/String;)V
#13 = Methodref #2.#56 // CctvTest.heoll2:(I)I
#14 = Methodref #2.#57 // CctvTest.heoll3:(Ljava/lang/String;)Ljava/lang/String;
#15 = String #58 //
#16 = Class #59 // java/lang/Object
#17 = Utf8 <init>
#18 = Utf8 ()V
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 LocalVariableTable
#22 = Utf8 this
#23 = Utf8 LCctvTest;
#24 = Utf8 main
#25 = Utf8 ([Ljava/lang/String;)V
#26 = Utf8 args
#27 = Utf8 [Ljava/lang/String;
#28 = Utf8 cctvTest
#29 = Utf8 z
#30 = Utf8 I
#31 = Utf8 heoll
#32 = Utf8 (Ljava/lang/String;)V
#33 = Utf8 str
#34 = Utf8 Ljava/lang/String;
#35 = Utf8 i
#36 = Utf8 heoll2
#37 = Utf8 (I)I
#38 = Utf8 c
#39 = Utf8 b
#40 = Utf8 heoll3
#41 = Utf8 (Ljava/lang/String;)Ljava/lang/String;
#42 = Utf8 hl
#43 = Utf8 SourceFile
#44 = Utf8 CctvTest.java
#45 = NameAndType #17:#18 // "<init>":()V
#46 = Utf8 CctvTest
#47 = NameAndType #31:#32 // heoll:(Ljava/lang/String;)V
#48 = Class #60 // java/lang/System
#49 = NameAndType #61:#62 // out:Ljava/io/PrintStream;
#50 = Utf8 java/lang/StringBuilder
#51 = Utf8 欢迎光临
#52 = NameAndType #63:#64 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#53 = NameAndType #65:#66 // toString:()Ljava/lang/String;
#54 = Class #67 // java/io/PrintStream
#55 = NameAndType #68:#32 // println:(Ljava/lang/String;)V
#56 = NameAndType #36:#37 // heoll2:(I)I
#57 = NameAndType #40:#41 // heoll3:(Ljava/lang/String;)Ljava/lang/String;
#58 = Utf8
#59 = Utf8 java/lang/Object
#60 = Utf8 java/lang/System
#61 = Utf8 out
#62 = Utf8 Ljava/io/PrintStream;
#63 = Utf8 append
#64 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#65 = Utf8 toString
#66 = Utf8 ()Ljava/lang/String;
#67 = Utf8 java/io/PrintStream
#68 = Utf8 println
{
//默认构造函数 <init>
public CctvTest(); //方法昵称
descriptor: ()V //参数即返回
flags: ACC_PUBLIC //修饰关键字
Code: //字节码指令集
//stack 操作数栈 深度
//locals 局部变量表
//args_size 参数个数
stack=1, locals=1, args_size=1
//指令下标:操作指令 运行时常量池符号引用
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable: //java 代码行 与字节码指令行引用
line 3: 0
LocalVariableTable: //局部变量表
Start Length Slot Name Signature
0 5 0 this LCctvTest;
//以下结构相同
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: new #2 // class CctvTest
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: bipush 19
10: istore_2
11: aload_1
12: ldc #4 // String hl
14: invokevirtual #5 // Method heoll:(Ljava/lang/String;)V
17: return
LineNumberTable:
line 5: 0
line 6: 8
line 7: 11
line 8: 17
LocalVariableTable:
Start Length Slot Name Signature
0 18 0 args [Ljava/lang/String;
8 10 1 cctvTest LCctvTest;
11 7 2 z I
public void heoll(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=2
0: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #7 // class java/lang/StringBuilder
6: dup
7: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
10: ldc #9 // String 欢迎光临
12: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: aload_0
26: bipush 10
28: invokevirtual #13 // Method heoll2:(I)I
31: istore_2
32: return
LineNumberTable:
line 12: 0
line 13: 25
line 14: 32
LocalVariableTable:
Start Length Slot Name Signature
0 33 0 this LCctvTest;
0 33 1 str Ljava/lang/String;
32 1 2 i I
public int heoll2(int);
descriptor: (I)I
flags: ACC_PUBLIC
Code:
stack=2, locals=5, args_size=2
0: iconst_1
1: istore_2
2: iconst_3
3: istore_3
4: aload_0
5: ldc #4 // String hl
7: invokevirtual #14 // Method heoll3:(Ljava/lang/String;)Ljava/lang/String;
10: astore 4
12: iload_2
13: iload_3
14: iadd
15: iload_1
16: iadd
17: ireturn
LineNumberTable:
line 17: 0
line 18: 2
line 19: 4
line 20: 12
LocalVariableTable:
Start Length Slot Name Signature
0 18 0 this LCctvTest;
0 18 1 i I
2 16 2 c I
4 14 3 b I
12 6 4 str Ljava/lang/String;
public java.lang.String heoll3(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=2
0: iconst_1
1: istore_2
2: iconst_3
3: istore_3
4: new #7 // class java/lang/StringBuilder
7: dup
8: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
11: aload_1
12: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: ldc #15 // String
17: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: invokevirtual #11 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
23: areturn
LineNumberTable:
line 25: 0
line 26: 2
line 27: 4
LocalVariableTable:
Start Length Slot Name Signature
0 24 0 this LCctvTest;
0 24 1 hl Ljava/lang/String;
2 22 2 c I
4 20 3 b I
}
SourceFile: "CctvTest.java"
后面会讲解整体的java 类执行过程就能更好的理解
执行引擎
本地方法接口
本地方法库
java 代码 执行过程
其他虚拟机
早期java 出过:
- Classic VM 已淘汰
- Exact VM 已淘汰
- HotSpot VM 目前jdk 默认虚拟机
- KVM 几乎淘汰,因为java ME 做终于移动应用方面,早期洛基亚 塞班系统上门的游戏java 就是运行在KVM 虚拟机上现在…这部分市场…还有吗…
当然还有其他虚拟机例如:
- JRockit (BEA公司)后来被oracle收购,然后差不多jdk8已经完成了 HotSpot 整合
- J9 VM (IBM公司)