JVM
2025-07-22 17:20:05 0 举报
AI智能生成
JVM
作者其他创作
大纲/内容
模型图
内存模型
堆
存放对象实例,也就是实际内存中的对象
方法区(元空间)
存放常量、静态变量、类信息
常量
静态常量
使用static+final修饰
成员常量
使用final修饰
局部常量
在方法内部,使用final修饰
静态变量
使用static修饰的变量
类信息
主要就是类的头信息
GC分代年龄
锁的标志位
栈
每个方法执行或被调用创建一个栈帧
局部变量表
存储方法参数和方法内部定义的局部变量
操作数栈
用于存储操作数和方法运行期间的计算结果
动态链接
每个栈帧包含一个指向运行时常量池中该栈帧所属方法的引用,用于支持方法调用过程中的动态链接
方法出口(方法返回地址)
记录方法结束后cpu返回该继续执行指令的位置。
异常退出时,返回地址通过异常处理器表来确定
本地方法栈
在线程需要调用本地方法时使用,如:调用底层C++的代码,使用本地方法栈为C++代码执行分配内存空间
程序计数器
每个线程都拥有一个,记录cpu执行到当前线程的哪一行指令
指示下一条指令的地址
程切换时保持状态,保证线程切换后可以正确的恢复到上一个线程执行的位置
对象创建过程
类加载检查
判断是否加载
否:加载类
是:分配内存
初始化零值
设置对象头
初始化init
HotSpot虚拟机
解释执行
来一个指令翻译一个
编译执行
将字节码指令提前编译好放入缓存中,执行时直接从缓存中获取
热点代码识别
方法调用计数器(Invocation Counter)
统计方法执行次数,当执行次数超过阈值,设置为热点代码
默认阈值是10000次
可以通过 -XX:CompileThreshold来设定
当一个方法被调用时,虚拟机会先检查该方法是否存在被即时编译过的版本,如果存在,则优先使用编译后的本地代码来执行。如果不存在已被编译过的版本,则将该方法的调用计数器值加一
回边计数器(Back Edge Counter)
它的作用是统计一个方法中循环体代码执行的次数
回边计数器在服务端模式下默认的阈值是 10700
编译优化技术
方法内联 inline
方法内联的优化行为就是把目标方法的代码复制到发起调用的方法之中,避免发生真实的方法调用。这样就可以减少频繁创建栈帧的性能开销。
逃逸分析 Escape Analysis
一个对象不会逃逸到方法或线程之外,那么 JIT 就可以为这个对象实例采取后续一系列的优化措施。
没有被外部其他方法调用
没有被其他线程访问
标量替换(Scalar Replacement)
若一个数据已经无法再分解成更小的数据来表示了,Java虚拟机中的原始数据类型(int、long等数值类型及reference类型等)都不能再进一步分解了,那么这些数据就可以被称为标量。
并且这个对象可以被拆散
根据程序访问的情况,将其用到的成员变量恢复为原始类型来访问,这个过程就称为标量替换。
将对象拆分后,可以让对象的成员变量在栈上分配和读写
锁消除 Lock elision
这个优化措施主要是针对 synchronized 关键字。当 JVM 检测到一个锁的代码不存在多线程竞争时,会对这个对象的锁进行锁消除
垃圾收集器
垃圾收集算法
标记复制算法
将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。
标记清除算法
标记存活的对象, 统一回收所有未被标记的对象(一般选择这种);也可以反过来,标记出所有需要回收的对象
标记整理算法
在标记清除算法的基础上,让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
年轻代
Serial
单线程垃圾收集器,回收时全过程STW
Serial Old收集器是Serial收集器的老年代版本
新生代采用复制算法,老年代采用标记-整理算法。
Parallel
Serial收集器的多线程版本,回收全过程STW
Parallel Old收集器是Parallel Scavenge收集器的老年代版本
新生代采用复制算法,老年代采用标记-整理算法。
ParNew
ParNew收集器其实跟Parallel收集器很类似,区别主要在于它可以和CMS收集器配合使用。
新生代采用复制算法
老年代
Serial Old
Parallel Old
CMS
标记-清除”算法实现的
初始标记
暂停所有的其他线程(STW),并记录下gc roots直接能引用的对象,速度很快。
并发标记
并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程
与用户线程同步执行
重新标记
为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录(主要是处理漏标问题)
主要用到三色标记里的增量更新算法(见下面详解)做重新标记。
STW
并发清理
开启用户线程,同时GC线程开始对未标记的区域做清扫
并发重置
重置本次GC过程中的标记数据
不分代
G1
从JVAV9开始的默认垃圾回收器
适用于大型堆和需要可预测停顿时间的应用
内存区域划分
将内存区域分为一个个的Region,最多分为2048个Region
一般Region大小等于堆大小除以2048
每个Region标有分区
Eden
新生代Eden区,存放刚刚创建的对象
Survivor
新生代Survivor区,存放经历过几次年轻代GC的对象
Old
老年代Old区,存放超过15次(默认)Young GC仍未被回收的对象
Humongous
专门存放大对象
判定规则:对象大小超过了Region的50%
如果一个对象过大可能会横跨多个连续的Region存放
GC过程
初始标记
暂停所有的其他线程,并记录下gc roots直接能引用的对象,速度很快;
并发标记
并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程
与用户线程同步执行
最终标记
为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录(主要是处理漏标问题)
主要用到三色标记里的增量更新算法(见下面详解)做重新标记。
STW
筛选回收
根据Region的回收价值和成本进行排序
根据用户所期望的GC停顿STW时间来制定回收计划
可以用JVM参数 -XX:MaxGCPauseMillis指定STW时间
回收算法主要用的是复制算法,将一个region中的存活对象复制到另一个region中
G1收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region
回收价值计算(大概): 回收的空间 / 回收的时间
就是用最短时间可以回收相对空间最多的Region
垃圾回收分类
Young GC
Eden区放满,并且G1计算回收时间接近参数 -XX:MaxGCPauseMillis设定的值,触发
如果回收时间远小于设定的STW时间,则分配新的Region给新生代使用
Mixed GC
老年代占比达到参数(-XX:InitiatingHeapOccupancyPercent)设定的值则触发,回收所有区域
主要使用复制算法,把存活的对象copy到其他Region中
如果没有足够的空闲Region来承载复制的对象,触发Full GC
Full GC
停止系统,采用单线程进行标记、清理和压缩整理(类似于Serial收集器)
ZGC
GC过程
并发标记(Concurrent Mark)
并发预备重分配(Concurrent Prepare for Relocate)
并发重分配(Concurrent Relocate)
并发重映射(Concurrent Remap)
调优
0 条评论
下一页