JVM和GC原理
2022-05-04 10:51:03 13 举报
AI智能生成
Java内存介绍,jvm和垃圾回收
作者其他创作
大纲/内容
Java代码执行
代码编译
javac
类加载
当程序主动使用某个类时,如果该类还未被加载到内存中,
则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。
如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化
当程序主动使用某个类时,如果该类还未被加载到内存中,
则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。
如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化
类的生命周期
加载
在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的入口
在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的入口
class
jar
war
zip
连接
验证
确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求
并且不会危害虚拟机自身的安全
确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求
并且不会危害虚拟机自身的安全
文件格式验证
元数据验证
字节码验证
符号引用验证
准备
为类变量分配内存,准备初始值
对final的静态字面值常量直接赋初值
为类变量分配内存,准备初始值
对final的静态字面值常量直接赋初值
解析
将符号引用转换成直接引用
将符号引用转换成直接引用
符号引用
- 符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟 机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引 用的字面量形式明确定义在 Java 虚拟机规范的 Class 文件格式中。
- CONSTANT_Class_info
- CONSTANT_Field_info
- CONSTANT_Method_info
直接引用
直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有 了直接引用,那引用的目标必定已经在内存中存在。
直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有 了直接引用,那引用的目标必定已经在内存中存在。
初始化
为静态变量赋值
为静态变量赋值
不初始化的场景(不初始化不代表不执行上面的加载和链接步骤)
- 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
- 定义对象数组,不会触发该类的初始化。
- 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触
- 发定义常量所在的类。
- 通过类名获取 Class 对象,不会触发类的初始化。
- 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。
- 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作。
使用
卸载
类加载的时机
主动引用
加载(loading)阶段,java虚拟机规范中没有进行约束,但初始化阶段,
java虚拟机严格规定了有且只有如下5种情况必须立即进行初始化(初始化前,必须经过加载、验证、准备阶段)
加载(loading)阶段,java虚拟机规范中没有进行约束,但初始化阶段,
java虚拟机严格规定了有且只有如下5种情况必须立即进行初始化(初始化前,必须经过加载、验证、准备阶段)
- 使用new实例化对象时,读取和设置类的静态变量、静态非字面值常量(静态字面值常量除外)时,调用静态方法时。
- 对内进行反射调用时。
- 当初始化一个类时,如果父类没有进行初始化,需要先初始化父类。
- 启动程序所使用的main方法所在类
- 当使用1.7的动态语音支持时。
被动引用
- 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
- 定义对象数组和集合,不会触发该类的初始化
- 类A引用类B的static final常量不会导致类B初始化(注意静态常量必须是字面值常量,否则还是会触发B的初始化)
类加载的方式
隐式加载
- 创建类对象
- 使用类的静态域
- 创建子类对象
- 使用子类的静态域
- 在JVM启动时,BootStrapLoader会加载一些JVM自身运行所需的class
- 在JVM启动时,ExtClassLoader会加载指定目录下一些特殊的class
- 在JVM启动时,AppClassLoader会加载classpath路径下的class,以及main函数所在的类的class文件
显示加载
- ClassLoader.loadClass(className),只加载和连接、不会进行初始化
- Class.forName(String name, boolean initialize,ClassLoader loader); 使用loader进行加载和连接,根据参数initialize决定是否初始化。
执行class
解释执行
编译执行
内存管理
内存空间
线程私有
程序计数器
PC
PC
指向虚拟机字节码指令的位置
唯一一个无OOM的区域
虚拟机栈
VM stack
VM stack
虚拟机栈和线程的生命周期相同
一个线程每调用一次方法就创建一个栈祯
栈祯的结构
本地变量表
操作数栈
运行时常量池的引用
返回地址
异常
线程请求的栈深度大于JVM所允许的深度 StackOverflowErroe
无法申请足够的内存 OOM
本地方法栈
Native Method Stack
Native Method Stack
线程共享
永久代
存储被JVM加载的类信息,常量,静态变量,即时编译后的代码
java8之后被元数据取代,使用直接内存,字符串池和静态变量放到堆中管理
存储被JVM加载的类信息,常量,静态变量,即时编译后的代码
java8之后被元数据取代,使用直接内存,字符串池和静态变量放到堆中管理
运行时常量池
堆
由于现代 VM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以细分为:
新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代
由于现代 VM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以细分为:
新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代
新生代
复制算法
复制算法
eden
from survivor
to survivor
老年代
标记清除算法
标记清除算法
直接内存
不受jvm gc 管理
内存分配
堆上分配
TLAB
栈上分配
内存回收GC
GC要做的三件事?
那些内存需要回收
什么时候回收
怎么回收
哪些对象已经死亡
引用计数法
Reference Count
Reference Count
循环引用问题
根搜索算法
GC Roots tracing
通过一系列成为GC roots的点作为起点,向下搜索
当一个对象到任何GC roots没有引用链,说明起已经死亡
GC Roots tracing
通过一系列成为GC roots的点作为起点,向下搜索
当一个对象到任何GC roots没有引用链,说明起已经死亡
VM 栈中的引用
方法区中的静态引用
JNI中的引用
垃圾回收算法
每次清除新生代,年龄+1.大于15直接放入老年代
对象占用内存太大,直接到老年代
老年代放不下,触发Major GC
每次清除新生代,年龄+1.大于15直接放入老年代
对象占用内存太大,直接到老年代
老年代放不下,触发Major GC
复制算法
copying
效率高,无碎片化,空间被压缩,利用率不高
如果存活对象增多,效率会变低
copying
效率高,无碎片化,空间被压缩,利用率不高
如果存活对象增多,效率会变低
新生代,朝生夕死
标记清除
Mark-Sweep
内存碎片化
Mark-Sweep
内存碎片化
标记需要清理的对象
清除对象
标记整理
Mark-Compact
Mark-Compact
标记
将待清除的移动到另外一边
清理边上的对象
分代收集
垃圾回收器
Serial
单线程,复制算法,新生代,对于单核的处理器,少了上下文切换,效率高
ParNew
多线程版本的复制算法,在回收stop the world时是多线程的
Parallel Scavenge
多线程复制算法,关注吞吐
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),
高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)),
高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而不需要太多交互的任务
Serial Old
老年代,单线程标记整理算法
与新生代Parallel Scavenge搭配使用
作为老年代CMS的备用收集器
与新生代Parallel Scavenge搭配使用
作为老年代CMS的备用收集器
Parallel Old
Parallel Scavenge的老年代版本,多线程标记整理算法。
在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只 能保证新生代的吞吐量优先,无法保证整体的吞吐量,Parallel Old 正是为了在年老代同样提供吞 吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,可以优先考虑新生代 Parallel Scavenge 和年老代 Parallel Old 收集器的搭配策略。
在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只 能保证新生代的吞吐量优先,无法保证整体的吞吐量,Parallel Old 正是为了在年老代同样提供吞 吐量优先的垃圾收集器,如果系统对吞吐量要求比较高,可以优先考虑新生代 Parallel Scavenge 和年老代 Parallel Old 收集器的搭配策略。
CMS
并发标记清除
老年代多线程标记清楚算法,以获取最短停顿时间
并发标记清除
老年代多线程标记清楚算法,以获取最短停顿时间
初始标记
只标记GC Roots能直接关联的对象,速度很快 stop the world
并发标记
进行GC roots跟踪过程,和用户线程一起工作,不需要stw
重新标记
修正并发标记时,因用户线程继续运行而导致标记产生的变动的那一部分标记记录,STW
并发清除
并发清除对象,和用户线程一起工作,不需要STW,时间比较长
G1
参数
Xms
Xmx
Xmn
-XX:PrintCDetails
-XX:SurvivorRatio=8
-XX:PretenureSizeThreshold=xxx
-XX:MaxTenuringThreshold
-XX:-HandlePromotionFailure
内存状况分析
jconsole
visualvm
jstat
jmap
MAT
JMM
Subtopic
Subtopic
0 条评论
下一页