主要总结jvm中的垃圾收集算法和垃圾收集器
文章目录
垃圾收集算法:
标记-清除算法
标记-复制算法
标记-整理算法
一、jvm实用工具
1.查看和修改jvm配置
# 查看14进程的所有jvm配置
jinfo -flags 14
# 查看某一项配置,比如查看dump文件路径
jinfo -flag HeapDumpPath 14
# 增加gc日志打印相关的jvm配置(删除则使用-号)
jinfo -flag +PrintGC 14 // 开启gc日志
jinfo -flag +PrintGCDetails 14 // 打印详情
jinfo -flag +PrintGCDateStamps 14 // 格式化时间
jinfo -flag +HeapDumpBeforeFullGC 14 // fgc前dump
jinfo -flag HeapDumpPath=/logs/dump_xxx.bin 14 // 设置dump路径
如果不清楚具体的配置项的名称,可以通过以下命令进行过滤查找(以下manageable为关键字):
java -XX:+PrintFlagsFinal -version | grep manageable
2.使用MAT工具打开后发现占用内存很少
默认不显示不可达对象。打开MAT的设置,reference,保留不可达的对象。如果实时设置不生效,尝试重新打开dump文件。
二、垃圾收集算法
1.标记-清除算法
2.标记-复制算法
3.标记-整理算法
三、垃圾收集器
垃圾收集器也分为新生代收集器和老年代收集器,目前比较常用的有以下,在jvm运行时几乎也都是两个收集器同时配置运行:
新生代收集器
1.Serial
最基础、历史最悠久的收集器。曾经在1.3是HotSpot新生代收集器的唯一选择。
采用复制算法。
生产组合:Serial/Serial Old
2.ParMew
实质上是Serial收集器的多线程并行版本,很多Serial收集器的jvm控制参数、算法、分配规则等也都完全一致。
采用复制算法。
生产组合:ParNew/Serial Old
ParNew/CMS
3.Parallel Scavenge
一款新生代收集器,基于标记-复制算法实现,也是能够并行收集的多线程收集器。CMS等收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(throughput),也被称为“吞吐量优先收集器”。
吞吐量 = 运行用户代码时间 / 运行用户代码时间 + 运行垃圾收集时间
生产组合:Parallel Scavenge / Parallel Old
总结:新生代收集器常用复制算法
老年代收集器
1.Serial Old
作为Serial收集器的老年代版本,是一个单线程收集器,使用标记-整理算法。
生产组合:Serial/Serial Old
2.Parallel Old
是Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现,1.6才开始提供。
生产组合:Parallel Scavenge / Parallel Old
3.CMS
CMS(Concurrent Mark Swep)并发-标记-清除收集算法是一种以获取最短回收停顿时间为把目标的收集器,是1.8版本常用的老年代回收器。
整个垃圾清理过程主要分为四步:
- 初始标记(CMS Initial Mark)
- 并发标记 CMS-concurrent
- 重新标记(CMS Final Remark)
- 并发清除CMS-concurrent-sweep
在打印出GC日志可以观察到如下GC过程:
2023-09-15T16:58:11.672+0800: [GC (CMS Initial Mark) [1 CMS-initial-mark: 643954K(701184K)] 661721K(777920K), 0.0067740 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2023-09-15T16:58:11.678+0800: [CMS-concurrent-mark-start]
2023-09-15T16:58:11.853+0800: [GC (Allocation Failure) 2023-09-15T16:58:11.853+0800: [ParNew: 75932K->8512K(76736K), 0.0570174 secs] 719886K->678318K(777920K), 0.0571620 secs] [Times: user=0.06 sys=0.00, real=0.06 secs]
2023-09-15T16:58:11.989+0800: [CMS-concurrent-mark: 0.252/0.310 secs] [Times: user=0.50 sys=0.00, real=0.31 secs]
2023-09-15T16:58:11.989+0800: [CMS-concurrent-preclean-start]
2023-09-15T16:58:11.995+0800: [CMS-concurrent-preclean: 0.006/0.006 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2023-09-15T16:58:11.995+0800: [CMS-concurrent-abortable-preclean-start]
2023-09-15T16:58:12.347+0800: [CMS-concurrent-abortable-preclean: 0.052/0.352 secs] [Times: user=0.10 sys=0.01, real=0.36 secs]
2023-09-15T16:58:12.349+0800: [GC (CMS Final Remark) [YG occupancy: 32938 K (76736 K)]2023-09-15T16:58:12.349+0800: [Rescan (parallel) , 0.0162113 secs]2023-09-15T16:58:12.365+0800: [weak refs processing, 0.0027552 secs]2023-09-15T16:58:12.368+0800: [class unloading, 0.1680965 secs]2023-09-15T16:58:12.536+0800: [scrub symbol table, 0.0499563 secs]2023-09-15T16:58:12.586+0800: [scrub string table, 0.0075458 secs][1 CMS-remark: 669806K(701184K)] 702744K(777920K), 0.2450178 secs] [Times: user=0.23 sys=0.01, real=0.24 secs]
2023-09-15T16:58:12.594+0800: [CMS-concurrent-sweep-start]
2023-09-15T16:58:12.770+0800: [CMS-concurrent-sweep: 0.176/0.176 secs] [Times: user=0.30 sys=0.03, real=0.18 secs]
2023-09-15T16:58:12.770+0800: [CMS-concurrent-reset-start]
2023-09-15T16:58:12.772+0800: [CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
jinfo -flags 15命令查询结果可以看到CMS的配置默认和ParNew组合使用:
生产组合:ParNew/CMS
混合收集器
1.G1
在G1出现之前,垃圾收集的目标范围要么是整个新生代(Minor GC),要么是整个老年代(Major GC),再要么就是整个java堆(Full GC)。而G1收集器是面向堆内存任何部分来组成回收集进行回收,衡量标准不是属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1收集器的Mixed GC模式。
G1将连续的java堆划分为多个大小相等的独立区域(Region),每个region都可以根据需要扮演新生代的Eden空间,Survivor空间或者老年代空间。
Region中还有一类特殊的Humongous区域,抓门用来存储大对象。G1认为只要大小超过一个Region容量一半的对象即可判定为大对象。
从整体上来看G1是基于“标记-复制算法”实现,但从局部(两个Region)之间上看又是基于“标记-整理”算法实现的,但都意味着G1运行期间不会产生内存空间碎片。
其他垃圾收集器:
- Shenandoah(低延时收集器)
- ZGC(低延时收集器,jdk11加入,基于Region,使用了读屏障、染色指针、内存多重映射等技术实现可并发的标标记-整理算法的)
- Epsilon