2.JVM运行机制
JVM启动流程
JVM基本结构
PC寄存器(程序计数器)
在虚拟机的概念模型里(仅是概念模式,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个寄存器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个寄存器来完成。
- 每个线程拥有一个PC寄存器
- 在线程创建时创建
- 指向下一条指令的地址
- 执行本地方法时,PC的值为undefined
方法区
在HotSpot上把GC分代收集扩展至方法区,或者说使用永久代来实现方法区。因此,我们得到了结论,永久代是HotSpot的概念,方法区是Java虚拟机规范中的定义,是一种规范,而永久代是一种实现。
- 保存装载的类信息
- 类型的常量池
- 字段,方法信息
- 方法字节码
- 通常和永久区(Perm)关联在一起
- HotSpots的Java8中,永久区已经被元空间替代。元空间并不在虚拟机中,而是使用本地内存。
Java堆
此内存区域的唯一目的就是存放对象实例,几乎所有对象实例都在这里分配内存。
- 和程序开发密切相关
- 应用系统对象都保存在Java堆中
- 所有线程共享Java堆
- 对分代GC来说,堆也是分代的
- GC的主要工作区间
Java栈
- 线程私有
- 栈由一系列帧组成(因此Java栈也叫做帧栈)
- 帧保存一个方法的局部变量、操作数栈、方法出口
- 每一次方法调用创建一个帧,并压栈;执行完成,则栈帧从虚拟机栈中出栈
直接内存
直接内存不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。
- 使用Native函数库直接分配对外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这快内存的引用进行操作
- 某些场景可以显著提高性能,避免在Java堆和Native堆中来回复制数据
- 直接内存+Jvm内存不能大于机器物理内存。如果使用了直接内存,设置-Xmx参数是需要考虑
内存模型
- 每一个线程有一个工作内存和主存独立
- 工作内存存放主存中变量的值的拷贝
- 可见性
- 一个线程修改了变量,其他线程可以立即知道
- 保证可见性的方法
- volatile
- synchronized (unlock之前,写变量值回主存)
- final(一旦初始化完成,其他线程就可见)
- 有序性
- 在本线程内,操作都是有序的
- 在线程外观察,操作都是无序的。(指令重排 或 主内存同步延时)
- 指令重排
- 线程内串行语义
- 写后读 a = 1;b = a; 写一个变量之后,再读这个位置。
- 写后写 a = 1;a = 2; 写一个变量之后,再写这个变量。
- 读后写 a = b;b = 1; 读一个变量之后,再写这个变量。
- 以上语句不可重排
- 编译器不考虑多线程间的语义
- 可重排: a=1;b=2;
- 线程内串行语义