对象的创建过程
1. class loading
2. class linking (verification, preparation, resolution)
- Verfication:验证文件是否符合JVM规定
- Preparation:静态成员变量赋默认值
- Resolution:将类、方法、属性等符号引用解析为直接引用
3. class initializing:调用类初始化代码,给静态成员变量赋初始值
4. 申请对象内存
5. 成员变量赋默认值
6. 调用构造方法<init>
- 成员变量顺序赋初始值
- 执行构造方法语句
普通对象
1. 对象头:markword 8
2. ClassPointer: -XX: +UseCompressedClassPointers为4字节,不开启为8字节
3. 实例数据 引用类型: -XX: +UseCompressedOops为4字节,不开启为8字节
4. padding对齐,8的倍数
数组对象
1. 对象头:markword 8
2. ClassPointer指针同上
3. 数组长度:4字节
4. 数组数据
5. padding对齐 8的倍数
markword 64位
1. hashCode部分
31位hashcode -> System.identityHashCode
按原始内容计算的hashcode,重写过的hashcode方法计算的结果不会存在这里。
无用的类:
1. 该类所有的实例都已经被回收,Java堆中不存在该类的任何实例。
2. 加载该类的ClassLoader已经被回收。
3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
GC垃圾回收的原理:判断出死亡的对象,然后清除死亡的,留下存活的。
- 引用计数法(Reference Counting)
- 可达性分析算法(Reachability Analysis)
通过"GC Roots"对象(线程栈变量、静态变量、常量池、JNI指针)作为起始点,然后从这些节点开始遍历所有引用链。如果某个对象没有GC Roots直接或间接连接的话,这个对象就被认为程序中不再使用可以被回收了。
垃圾回收的几种算法
- 标记清除
算法相对简单 存活对象比较多的情况下效率较高
两遍扫描,效率偏低 容易产生碎片
- 标记复制
适用于存活对象较少的情况,只扫描一次,效率提高,没有碎片
空间浪费,移动复制对象,需要调整对象引用
- 标记压缩
不会产生碎片,方便对象分配,不会产生内存减半
扫描两次,需要移动对象,效率偏低
程序计数寄存器(Program Counter Register),JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟。
- PC寄存器用来存储指向下一条指令的地址,即将要执行的指令代码。由执行引擎读取下一条指令。
- 在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程周期一致。
每个线程在创建的时候都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应一次次Java方法调用,是线程私有的,生命周期和线程一致。
作用:主管Java程序的运行,它保存方法的局部变量,部分结果,并参与方法的调用和返回。
Method Area
- Perm Space (<1.8) 字符串常量位于Perm Space FGC不会清理 大小启动的时候指定,不能变
- Meta Space (>=1.8) 字符串常量位于堆 会触发FGC清理 不设定的话,最大就是物理内存
JVM内存分代模型(用于分代垃圾回收算法)
- 部分垃圾回收器使用的模型
除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型
G1是逻辑分代,物理不分代
除此之外,不仅逻辑分代,而且物理分代
MinorGC/YGC: 年轻代空间耗尽时触发
MajorGC/FullGC: 在老年代无法继续分配空间时触发,新生代老年代同时进行回收
- 栈上分配
线程私有小对象
无逃逸
支持标量替换
无需调整
- 线程本地分配TLAB (Thread Local Allocation Buffer)
占用eden,默认1%
多线程的时候不用竞争eden就可以申请空间,提高效率
小对象
无需调整
对象分配过程
垃圾回收器
JDK1.8默认的垃圾回收:PS + ParallelOld
Serial: a stop-the-world, copying collector which uses a single GC thread
CMS的问题
1. Memory Fragment
2. Floating Garbage
算法:
CMS算法:三色标记 + Incremental Update
G1算法:三色标记 + SATB
ZGC算法:ColoredPointers + 写屏障
Shenandoah算法:ColoredPointers + 读屏障
调优系统CPU占用率100%
一定有线程在占用系统资源
- 找出哪个进程CPU高(top)
- 该进程中的哪个线程CPU高(top -Hp)
- 导出该线程的堆栈(jstack)
- 查找哪个方法(栈帧)消耗时间(jstack)
查找系统内存飚高问题
- 导出堆内存(jmap)
- 分析(jhat, jvisualvm, mat, jprofiler ...)
三色标记
- 白色:未被标记的对象
- 灰色:自身被标记,成员变量未被标记
- 黑色:自身和成员变量均已标记完成
产生漏标
1. 标记进行时增加了一个黑到白的引用,如果不重新对黑色进行处理,则会漏标
2. 标记进行时删除了灰对象到白对象的引用,那么这个白对象有可能被漏标
1. incremental update: 增量更新,关注引用的增加,把黑色重新标记为灰色,下次重新扫描属性(CMS)
2. SATB(snapshot at the beginning):关注引用的删除 当引用消失时,把这个引用推到GC的堆栈,保证D还能被GC扫描到(G1)
灰色 -> 白色引用消失时,如果没有黑色指向白色引用会被push到堆栈,下次扫描时拿到这个引用,由于有RSet的存在,不需要扫描整个堆去查找指向白色的引用,效率较高。
CMS执行步骤:初始标记、并发标记、重新标记、并发清理
G1产生FGC怎么做
1. 扩内存
2. 提高CPU性能(回收的快,业务逻辑产生对象的速度固定,垃圾回收越快,内存空间越大)
3. 降低MixedGC触发的阈值,让MixedGC提早发生(默认是45%)