垃圾回收器
一. GC分类与性能指标
垃圾回收器分类
按照线程数分
按照工作模式分
按照碎片处理方式或工作的内存区间分
评估GC的性能指标
吞吐量(throughput)
暂停时间(pause time)
二.不同的垃圾回收器概述
垃圾收集器发展史
GC 可以是两个名词的缩写
Garbage Collection 垃圾回收
Garbage Collector 垃圾回收器
七款经典的垃圾回收器
七款经典收集器与垃圾分代之间的关系
垃圾回收器的组合关系
在JDK8 中默认是Parallel Scavenge GC + Parallel Old GC的组合
为啥Parallel Scavenge GC 和 ParNew GC 都是并行的垃圾回收器但是.Parallel Scavenge GC 却不能和CMS GC组合呢 ?
因为Parallel Scavenge GC 底层用的GC框架和其他的垃圾回收器不要用,所以不能进行兼容
不同的垃圾回收器概述
如何查看默认的垃圾收集器
-XX:+PrintCommandLineFlags
任意执行一个程序
-XX:InitialHeapSize=264819328 -XX:MaxHeapSize=4237109248 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
可以看出使用的是Parallel Scavenge GC
上面的是JDK8 的, 下面是切换到JDK9之后执行的结果
使用的就变成G1了
三.Serial回收器: 串行回收
-XX:InitialHeapSize=264819328 -XX:MaxHeapSize=4237109248 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
/**
* -XX:+PrintCommandLineFlags
* -XX:+PrintCommandLineFlags -XX:+UseSerialGC 指定使用的GC
*/
public class GCUseTest {
public static void main(String[] args) throws InterruptedException {
ArrayList<byte[]> list = new ArrayList<>();
while (true) {
byte[] arr = new byte[100];
list.add(arr);
Thread.sleep(10);
}
}
}
四.ParNew回收器 : 并行回收
使用parNew和CMSGC, 在JDK9中会报错
五.Parallel回收器: 吞吐量优先
查看使用的GC
使用MaxGCPauseMills时会动态调节堆内存的大小, 如果堆内存变小了,可能会导致GC的次数变多,GC次数变多了之后,可能会导致吞吐量下降,影响整体性能
这个参数是默认开启状态的,这就是为什么我们在实际查看新生代和老年代的比例时不是8:1的原因
六.CMS回收器: 低延迟
CMS工作原理
CMS 收集器可以设置的参数
JDK后续版本中CMS的变化
七. G1 回收器: 区域化分代式
G1回收器的特点(优势)
G1回收器的缺点
G1回收器的参数设置
在使用MaxGCPauseMillis设置期望最大GC停顿时常的时候,如果设置的市场过于短了的话,可能会存在回收的速度比不上占用region的速度,导致内存占用率越来越高,并最终发生了Full GC ,得不偿失.反而导致性能下降了
GC时间在200-300ms差不多都是正常的时间段
G1回收器常见操作步骤
G1回收器的适用场景
分区Region: 化整为零
G1回收器垃圾回收过程
G1回收器垃圾回收过程: Remembered Set
G1回收过程一: 年轻代GC
G1回收过程二 : 并发标记过程
G1回收过程三: 混合回收
G1回收可选的过程四: Full GC
G1回收过程 : 补充
G1 回收器优化建议
八. 垃圾回收器总结
7种经典垃圾回收器总结
怎么选择垃圾回收器
面试
九. GC日志分析
Allocation Failure 分配失败
比如使用./logs/gc.log ,的话,需要在过程的根目录下建一个logs目录,然后每次执行的时候就会生成新的日志文件
Minor GC 日志
Full GC 日志
perm -> 永久代或者原空间
举例
/**
* 在jdk7 和 jdk8中分别执行
* 设置堆内存大小 新生代大小 新生代比例, 伊甸园区8份 使用串行垃圾回收器
* -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
*/
public class GCLogTest1 {
private static final int _1MB = 1024 * 1024;
public static void testAllocation() {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
allocation4 = new byte[4 * _1MB];
}
public static void main(String[] args) {
testAllocation();
}
}
在JDK7中运行
由图可以看出来, 在分配内存的过程中,首先是把前面三个2MB的对象放入伊甸园区之后, 当需要进行存放第四个4MB的对象的时候,会发现新生代已经没有办法进行存放了,这个时候就触发了一次GC, 将原本的3个2M的对象都放到了老年代之中, 这个时候就可以将新的4M的对象放到伊甸园区中了,查看打印的GC日志可以查看.
可以看出新生代使用了4M的空间,而老年代则使用了6M的大小
在JDK8中运行
在JDK8中大对象直接放入了老年代中
GC日志分析工具
GC Viewer
下载地址
直接打开jar文件即可使用
GC Easy
在线地址 : GC Easy
十. 垃圾回收器的新发展
JDK11 新特性
参考网址 : JEP318
318 -> 无操作的垃圾回收器,只进行分配内存的操作,不进行垃圾的回收(分配完内存就退出了)
Open JDK12的Shenandoah GC
ZGC – JDK14新特性
左边: 低延迟优先的情况下的吞吐量
右边: 低延迟不保证优先的情况下的吞吐量
无论是平均停顿时间还是95% 或者最大的停顿时间,ZGC都可以保证停顿时间在10ms以内