4.JVM-垃圾回收介绍
记录个人学习中记录笔记,如有错误请您指正,谢谢🙏
垃圾回收器发展史
传统垃圾回收: 分代回收 不同代有不同的垃圾回收机制
保底
标记清除算法
垃圾识别算法
引用计数法
缺陷:下图2 出现循环引用 无法解决
可达性分析
大部分(Java,python,go等)都在用这种方式
常见的根对象(GCRoots):
- 方法区中的常量引用的对象
- 方法区中的类静态属性引用的对象
- 本地方法栈中JNI的引用的对象
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
垃圾清除算法
标记-清除法 (基本不用)
问题: 容易产生无法使用的内存碎片
比如标记可用块每块2MB,实际使用1.9MB,剩余0.1MB就是内存碎片,无法处理
复制法 (适合新生代)
问题:浪费内存空间
存货对象少垃圾对象多的前提下效果好
标记-压缩算法 (适合老年代)
问题:速度慢
三种清除方法比较
新生代垃圾回收器
GC日志内容
/*** jdk17: -Xlog:gc*=info:stdout:time,uptime,level,tags* jdk8:* -Xmx20M* -Xmn10M* -XX:+PrintGCDetails* -XX:+UseSerialGC* -XX:+PrintGCTimeStamps* -XX:+PrintGCDateStamps # 绝对时间* -XX:SurvivorRatio=8*/
public class GC_Serial {/*-Xmx20M-Xmn10M-XX:+PrintGCDetails-XX:+UseSerialGC-XX:+PrintGCTimeStamps-XX:SurvivorRatio=8*/private static final int size = 1024 * 256;public static void main(String[] args) {
// System.gc();for (int i = 0; i < 30; i++) {byte[] date = new byte[size];}}/*** Allocation Failure: // 分配内存失败* metadata space exhausted: // 元数据空间耗尽* system.gc() invoked: // 调用了System.gc() => full gc*//*** 各种垃圾回收器常见的类型:* serial => DefNew* parNew => ParNew* Parallel => PSYoungGen* Parallel Old => ParoldGen*/}
- def new generation:新生代
- tenured generation:老年代
- metaspace:元空间
- committed:下次分配可以分配多大空间
- reserved:最大可用空间多大
0.709: [GC (Allocation Failure) 0.709: [DefNew: 8153K->731K(9216K), 0.0017122 secs] 8153K->731K(19456K), 0.0018048 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heapdef new generation total 9216K, used 3021K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K, 27% used [0x00000000fec00000, 0x00000000fee3c460, 0x00000000ff400000)from space 1024K, 71% used [0x00000000ff500000, 0x00000000ff5b6fc8, 0x00000000ff600000)to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)Metaspace used 3226K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 332K, capacity 386K, committed 512K, reserved 1048576K
Serial串行回收器-最基本的垃圾回收器
- 新生代 SerialNew 老年代 SerialOld
- 新生代使用
<font style="background-color:#FBDE28;">复制算法</font>
老年代采用<font style="background-color:#FBDE28;">标记压缩</font>
测试代码
/*-Xmx20M-Xmn10M-XX:+PrintGCDetails-XX:+UseSerialGC-XX:+PrintGCTimeStamps-XX:SurvivorRatio=8*/private static final int size = 1024 * 256;public static void main(String[] args) {
// System.gc();for (int i = 0; i < 30; i++) {byte[] date = new byte[size];}}/*** Allocation Failure: // 分配内存失败* metadata space exhausted: // 元数据空间耗尽* system.gc() invoked: // 调用了System.gc() => full gc*//*** 各种垃圾回收器常见的类型:* serial => DefNew* parNew => ParNew* Parallel => PSYoungGen* Parallel Old => ParoldGen*/
- def new generation:新生代
- tenured generation:老年代
- metaspace:元空间
- committed:下次分配可以分配多大空间
- reserved:最大可用空间多大
0.709: [GC (Allocation Failure) 0.709: [DefNew: 8153K->731K(9216K), 0.0017122 secs] 8153K->731K(19456K), 0.0018048 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heapdef new generation total 9216K, used 3021K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)eden space 8192K, 27% used [0x00000000fec00000, 0x00000000fee3c460, 0x00000000ff400000)from space 1024K, 71% used [0x00000000ff500000, 0x00000000ff5b6fc8, 0x00000000ff600000)to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)Metaspace used 3226K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 332K, capacity 386K, committed 512K, reserved 1048576K
ParNew回收器
- 只能在新生代使用,一般配合老年代 CMS 回收器,比Serial多了并行回收
测试代码
/*-Xmx20M-Xmn10M-XX:+PrintGCDetails-XX:+UseParNew-XX:+PrintGCTimeStamps-XX:SurvivorRatio=8*/private static final int size = 1024 * 256;public static void main(String[] args) {
// System.gc();for (int i = 0; i < 30; i++) {byte[] date = new byte[size];}}/*** Allocation Failure: // 分配内存失败* metadata space exhausted: // 元数据空间耗尽* system.gc() invoked: // 调用了System.gc() => full gc*//*** 各种垃圾回收器常见的类型:* serial => DefNew* parNew => ParNew* Parallel => PSYoungGen* Parallel Old => ParoldGen*/
Parallel回收器
JDK8默认的回收器
- Paralle和ParNew机制类似,通过自适应调节来决定回收时机、和回收频率,达到“吞吐量”有限
- 和ParNew的区别在于,进入“SafePoints”的时机不一样
- 在年轻代叫做ParalleNew 老年代叫做 ParalleOld
老年代垃圾回收期
Parallel Old:3.4部分介绍
Serial Old:3.2部分介绍
CMS回收器
没有一个jdk版本设置他默认清理器
JDK14已经移除
优点:
- 并发收集
- 停顿时间短
缺点:
- **处理器资源非常敏感: **收集过程中会开启多个线程
- 会产生浮动垃圾:在并发标记和并发清理阶段,用户线程产生的新垃圾,可能出现Concurrent Model Failure失败而导致另一次Fu GC的产生
- 空间碎片过多:因为采用的标记清除算法,会产生标记清除算法的问题
初始标记(step-1)
会STW
并发标记(step-2)
重新标记(step-3)
会STW
并发清除(step-4)
CMS 回收过程
初始标记
新生代引用的老年代的活的对象
并发标记
标记第一步找出的GC Roots节点,往下遍历
预清理阶段
将第一步第二步没有引用的节点提前标记,本质第一步第二步的增量操作
为的是减少重新标记的时间
可中断的预处理
把重新标记的工作提前做
重新标记
DirtyCard: 预清理阶段,检查看看是否需要清理
并发清理
并发重置
其他问题
MinorGC vs YoungGC
一样的概念,都是针对 年轻代或者叫做新生代的清理
FullGC vs OldGC
G1之前两者都是一样的,都是老年代的垃圾回收
FullGC 表示 新生代老年代永久代的清理更合理
MajorGC
含义不清
FullGC什么时候出发
System.gc()
手动触发FullGC