目录
10、ZGC到底比G1/CMS/ParallelGC好在哪儿?
1、OpenJDK8的默认GC是不是CMS/G1?
各个版本默认GC策略。
JDK 6-8:默认Parallel GC
JDK9-18:默认G1 GC
GC发展历史:
Serial GC->Parallel GC->CMS GC->G1 GC->ZGC/Shenandoah GC
其中8、11、17为长期维护版本LTS版本。其中CMS在JDK9后被标记为废弃,JDK14后删除。
2、默认最大堆内存是不是物理内存的1/4?
简单说,必须是1024的倍数,且不能低于2M。
内存很小时,最大堆内存是物理内存的一半,其他情况是1/4,注意32位系统下最大物理内存就只有4G的限制。32位机器,最大1G;64位机器,最大可以超过32G(物理内存超过128G)。
3、默认的年轻代最大值是不是一定是堆内存的1/3?
默认情况下,-XX:NewRatio默认是2,所以年轻代占比1(1+2) = 1/3
-Xmn/-XX:MaxNewSize,直接制定大小
G1:https://www.oracle.com/cn/technical-resources/articles/java/g1gc.html
G1默认是5%~60%:
-XX:G1NewSizePercent=5 -XX:G1MaxNewSizePercent=60
CMS很诡异。
CMS在内存较小时为:64M * 并发GC线程数(4) * 13 / 10 =332.8M
Young区内存最大值跟Xmx无关。如果上面计算的比Xmx的1/3大,则为1/3。
CMS:
Parallel GC和CMS GC的最大young区大小如何计算?
默认情况下,大小会受到自适应参数影响,我们先关掉此参数-XX:-UseAdaptiveSizePolicy。
然后试验如下:
java -Xmx1g -Xms1g -XX:-UseAdaptiveSizePolicy -XX:+UseParallelGC -jar target/gateway-server-0.0.1-SNAPSHOT.jar
MaxHeapSize = 1073741824 (1024.0MB)
NewSize = 357564416 (341.0MB)
MaxNewSize = 357564416 (341.0MB)
OldSize = 716177408 (683.0MB)
java -Xmx1g -Xms1g -XX:-UseAdaptiveSizePolicy -XX:+UseConcMarkSweepGC -jar target/gateway-server-0.0.1-SNAPSHOT.jar
MaxHeapSize = 1073741824 (1024.0MB)
NewSize = 348913664 (332.75MB)
MaxNewSize = 348913664 (332.75MB)
OldSize = 724828160 (691.25MB)
java -Xmx2g -Xms2g -XX:-UseAdaptiveSizePolicy -XX:+UseConcMarkSweepGC -jar target/gateway-server-0.0.1-SNAPSHOT.jar
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 348913664 (332.75MB)
MaxNewSize = 348913664 (332.75MB)
OldSize = 1798569984 (1715.25MB)
可以看到 ParallelGC下,young区大小为1024/3 = 341.3M,跟上述显示一致。
CMS情况下则为332.75M,不是1/3,并且在xmx为2048M时,还是332.75M,这说明最大young区大小与Xmx参数无关。
实际上,我的电脑上:64M * 并发GC线程数(4) * 13 / 10 =332.8M
这个式子是jvm代码写死的,只跟GC线程数有关系。
继续测试:
-XX:ParallelGCThreads=2
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 174456832 (166.375MB)
MaxNewSize = 174456832 (166.375MB)
OldSize = 1973026816 (1881.625MB)
-XX:ParallelGCThreads=8
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 697892864 (665.5625MB)
MaxNewSize = 697892864 (665.5625MB)
OldSize = 1449590784 (1382.4375MB)
证实了我们的猜测,并发线程为2时,大小为166M;线程为8时,为665M。
注意:线程数超过8的时候,会退化成按newRatio计算。
4、给JVM分配内存越多越好吗?
其他内存 = 虚拟机(少量) + 非堆 + 堆外
系统可用内存 = 全部内存 - 系统损耗(少量)
例如虚拟机或容器只有4G,如果配置-Xmx4g,JVM以为自己能用到4G,实际上堆内存到3.6G的时候,系统就没有内存了,这时候在想去使用内存,就一定会OOM。
1)永远建议 -Xmx=最大物理内存*[0.6~0.8]。物理内存4G,Xmx为2.4g~3.2g为佳。
2)堆越大,扫描复制的时间越长,建议在合适的范围内,尽量少一些内存。
5、堆外内存很小,所以不用管?
堆外内存 = 一般指Direct Memory,不受GC控制。
一般来说,JVM/Netty等都可能会使用堆外内存。
默认堆外内存可以跟堆内存一样大!!!
可以通过 -XX:MaxDirectMemorySize 限制。
6、所有的GC都会暂停(Stop The World)吗?
所有GC都可能会Stop The World:
- Serial GC/Parallel GC:全部停顿,差别只是串行和并行。
- CMS GC/G1:部分步骤停顿,其他步骤跟业务线程并发执行。(一个简单的分辨方式就是看GC日志里,步骤前面如果有Concurrent 就是并发执行,不STW,否则就是STW停顿。)
- ZGC/Shennandoah GC:大部分步骤都不暂停。
以办公区打扫卫生为例。
7、并发线程是CPU的1/8,并行线程是CPU的5/8吗?
GC是CPU密集型操作,所以垃圾回收使用所有CPU时效率最高。
ParallelGC时,因为STW了,所有CPU都可以用来做GC。
1. ParallelGCThreads 参数的默认值是:
CPU核心数 <= 8,则为 ParallelGCThreads=CPU核心数
CPU核心数 > 8,则为 ParallelGCThreads = CPU核心数 * 5/8 + 3 向下取整
16核的情况下,ParallelGCThreads = 13
32核的情况下,ParallelGCThreads = 23
64核的情况下,ParallelGCThreads = 43
并发GC时,只能使用一部分线程,其他线程留给业务线程做业务处理。
2. ConcGCThreads的默认值则为:
ConcGCThreads = (ParallelGCThreads + 3)/4 向下去整
ParallelGCThreads = 1~4时,ConcGCThreads = 1
ParallelGCThreads = 5~8时,ConcGCThreads = 2
ParallelGCThreads = 13~16时,ConcGCThreads = 4
8、是不是GC停顿越短系统性能就越好?
GC 是 吞吐量 和延迟 之间平衡的艺术。
一般情况下选择:
业务是关注吞吐还是延迟?堆内存是大还是小?
当然,相同的GC比如G1,在更高版本JDK表现更好。
低延迟的停顿,代价是业务运行的过程中,有一些线程/CPU拿去做垃圾处理了,所以整体来说,性能并不是最好。极端情况下,吞吐量ZGC 比G1 低15%,G1 可能比 Parallel GC少15%。
9、G1会不会发生长时间停顿的Full GC?
G1是否就一定比CMS/ParallelGC好?
Young GC:Young区满了。
Mixed GC:同时回收Young和Old。
Full GC:极端条件下退化成串行处理(JDK10后改成并行)。
详细过程今天就不聊了,大家自己复习。注意处理的步骤,参数和阈值。
10、ZGC到底比G1/CMS/ParallelGC好在哪儿?
非常低的延迟,适用于内存较大,延迟敏感的系统。
示例:32 G堆平均GC不到 3 ms
其他:
吞吐量问题:极端情况比G1低。
返还系统内存:xmx=xms时不时问题。
指针压缩:内存用量要比其他GC大。
JDK15全系统:JDK12-14之有linux版本的OpenJDK有ZGC。
对于不同GC的常用场景总结
选择正确的GC算法,唯一可行的方式就是去尝试,并找出不合理的地方,一般性的指导原则:
- 如果考虑吞吐优先,CPU尽量都用于处理业务,用 Parallel GC;
- 如果考虑有限的低延迟,且每次GC时间尽量短,用 CMS GC;
- 如果堆较大,同时希望整体来看平均GC时间可控,使用 G1 GC。
对于内存大小的考量:
- 一般4G以上,算是比较大,用 G1 GC 的性价比较高。
- 一般超过8G,比如16G-64G内存,非常推荐使用 G1 GC。
- 更大内存或者低延迟要求非常苛刻,用 ZGC 。