文章目录
- 经典垃圾收集器
- Serial收集器
- ParNew收集器
- Parallel Scavenge收集器
- 参数
- Serial Old收集器
- Parallel Old收集器
- CMS收集器
- CMS的三个缺点:
- CMS的参数:
- Garbage First收集器
- G1收集器设计的难点
- G1收集器收集垃圾的过程
- G1优缺点
- 相比cms的优点
- 相比cms的缺点
- 总结
- 总结
经典垃圾收集器
《Java虚拟机规范》中对垃圾收集器应该如何实现并没有做出任何规定,因此不同的厂商、不同版本的虚拟机所包含的垃圾收集器都可能会有很大差别,不同的虚拟机一般也都会提供各种参数供用户根据自己的应用特点和要求组合出各个内存分代所使用的收集器。
连线的垃圾收集器之间可以配合使用
Serial收集器
Serial收集器是一个单线程工作的收集器,它在进行垃圾收集时必须暂停其他所有线程直到它收集完毕
迄今依然是HotSpot虚拟机运行在客户端模式下的默认新生代收集器,简单高效,在内存资源受限的环境是所有收集器额外内存消耗最小的
ParNew收集器
ParNew是Serial收集器的多线程版本,除了多线程运行外与Serial别无二致,下边三个参数和Serial相同
-XX:SurvivorRatio
-XX:PretenureSizeThreshold
-XX:HandlePromotionFailure
运行在服务端模式下的新生代首选收集器,尤其是JDK 7之前的遗留系统中首选的新生代收集器,其中有一个与功能、性能无关但其实很重要的原因是:除了Serial收集器外,目前只有它能与CMS收集器配合工作。
ParNew是激活CMS收集器后默认的新生代收集器。
JDK9以后ParNew只能配合CMS使用,作为CMS的新生代收集器
它默认开启的收集线程数与处理器核心数量相同,在处理器核心非常多(譬如32个,现在CPU都是多核加超线程设计,服务器达到或超过32个逻辑核心的情况非常普遍)的环境中,可以使用-XX:ParallelGCThreads
参数来限制垃圾收集的线程数。
激活CMS:-XX:+UseConcMarkSweepGC
禁用或激活ParNew:-XX:+/-UseParNewGC
JDK9去掉+命令
Parallel Scavenge收集器
Parallel Scavenge收集器也是一款新生代收集器,同样是基于标记-复制算法实现的收集器,也是能够并行收集的多线程收集器
Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)
吞吐量=运行用户代码时间/(运行用户代码时间+运行垃圾收集时间)
停顿时间越短就越适合需要与用户交互或需要保证服务响应质量的程序,良好的响应速度能提升用户体验;而高吞吐量则可以最高效率地利用处理器资源,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的分析任务
参数
-
-XX:MaxGCPauseMillis
:最大GC时间,这个值越小垃圾收集越快,但是是以吞吐量和新生代空间为代价换取的- 最大GC时间越小,新生代空间越小,收集越快,垃圾收集次数增加,吞吐量越小
- 越来新生代500m,十秒钟回收1次,每次100毫秒,变成5秒钟回收一次,每次70毫秒
-
-XX:GCTimeRatio
:GC时间占总时间的比率- 设为19的话就是1/(1+19)=1/20 5%,默认99;
-
-XX:+UseAdaptiveSizePolicy
:垃圾自适应调节开关- 如果对垃圾收集不太了解,手工优化存在困难,就设置好最大堆容量和-XX:MaxGCPauseMillis或-XX:GCTimeRatio后开启自适应调节
Serial Old收集器
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。这个收集器的主要意义也是供客户端模式下的HotSpot虚拟机使用。如果在服务端模式下,它也可能有两种用途:一种是在JDK 5以及之前的版本中与Parallel Scavenge收集器搭配使用,另外一种就是作为CMS收集器发生失败时的后备预案,在并发收集发生Concurrent Mode Failure时使用
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现、
在注重吞吐量或者处理器资源较为稀缺的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器这个组合
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。基于标记清除算法,它的运作比前边几个更复杂一些,一共分为以下四个步骤
- 初始标记
- 标记一下能直接关联到GCRoots的对象,速度很快,需要暂停用户线程
- 并发标记
- 从GCRoots 遍历整个对象图的过程,时间比较久,但是不需要停止用户线程
- 重新标记
- 修正并发标记时期用户线程运作导致标记变动的对象的标记记录,需要暂停用户线程
- 并发清除
- 清除标记的对象
CMS的三个缺点:
- CMS收集器对处理器资源非常敏感
- cms默认线程数是(核心数+3)/4,在4个核心以上时只占用cpu不到25%的资源,在只有4个核心以下时会对用户线程影响很大
- CMS收集器无法处理“浮动垃圾”
- cms收集垃圾时用户线程仍然会运行产生浮动垃圾,所以也需要给用户线程预留内存空间和浮动垃圾占用的空间,这就导致在CMS并发收集 的时候如果内存不够用户为对象分配空间的话就会发成Concurrent Mode Failure,失败之后激活SerialOld进行老年代垃圾收集。jdk6之前cms在老年代使用68%时激活,之后默认92%,可通过
-XX:CMSInitiatingOccupancyFraction
修改
- cms收集垃圾时用户线程仍然会运行产生浮动垃圾,所以也需要给用户线程预留内存空间和浮动垃圾占用的空间,这就导致在CMS并发收集 的时候如果内存不够用户为对象分配空间的话就会发成Concurrent Mode Failure,失败之后激活SerialOld进行老年代垃圾收集。jdk6之前cms在老年代使用68%时激活,之后默认92%,可通过
- 因为CMS收集器基于标记清除算法,标记清除算法会产生大量内存碎片导致无法给较大对象分配内存而触发FullGC
CMS的参数:
-
-XX:CMSInitiatingOccupancyFraction
:在老年代被占用多少百分比后gc -
-XX:+UseCMSCompactAtFullCollection
:在 CMS 要进行 Full GC 时进行内存碎片整理(默认开启)jdk9废弃 -
-XX:CMSFullGCsBeforeCompaction
:在多少次 Full GC 后进行一次空间整理(默认是 0,即每一次 Full GC 后都进行一次空间整理)jdk9废弃
Garbage First收集器
G1也仍是遵循分代收集理论设计的,是一款主要面向服务端应用的垃圾收集器。G1收集的设计者希望设计出一款能够建立停顿时间模型的收集器,停顿时间模型大意是指在M时间内消耗在垃圾收集的时间不超过N秒。设定的目标是在延迟可控的情况下获得尽可能高的吞吐量。
为了实现这个目标,G1收集器不在以年代划分收集区域,而是以哪个区域的垃圾最多,回收价值最大作为标准,这就G1Mixed GC模式。
G1收集器将内存划分为一个个等大连续的小块(Region),每个Region都可以根据需要被扮演Eden,Survivor,老年代空间,Region中还有一类特殊的Humongous区域,专门用来存储大对象,只要大小超过region一半的对象就会被当成大对象,而对于那些超过了整个Region容量的超级大对象,将会被存放在N个连续的Humongous Region之中,G1大多数行为都把HumongousRegion当成老年代看待
-XX:G1HeapRegionSize
:可以调节每个region的大小,取值范围1M~32M,且应为2的次幂
G1之所以能够建立停顿时间模型,是因为G1是以Region为基本单位回收的,根据每个Region的垃圾数量和回收时间维护一个优先列表。每次根据用户设定允许的收集停顿时间(使用参数-XX:MaxGCPauseMillis
指定,默认值是200毫秒),优先处理回收价值收益最大的那些Region
G1收集器设计的难点
- 每个Region都需要维护一个别的Region指向自己的记忆集,Region的记忆集相当于一种Hash表,键是指向自己的指针的Region的首地址,值是一个集合,里边存着卡表的索引号,这种记忆集比原来复杂的多,同时分代数量也比之前多,因此G1收集器有着比传统收集器更高的内存占用负担
- G1如何保证用户线程与收集线程互不干扰,G1在每个region里设置了两个指针(TAMS),新建对象的分配在这两个指针划分的区域里边,这些对象默认为黑色。在并发标记完成后使用原始快照修正被用户线程更改的标记关系,如果回收速度赶不上对象分配速度就会冻结用户线程
- 怎么样算出回收效益最大的region,计算出每个region的衰减平均值,衰减平均值和普通平均值不同的地方在于衰减平均值计算的最近的平均状态,然后根据衰减平均值在满足不超过停顿时间的条件下选出region完成手机
G1收集器收集垃圾的过程
- 初始标记
- 标记一下GC Roots能直接关联到的对象,并修改TAMS指针的值,修改指针是在minor gc 的时候顺便完成的所以这步几乎没有停顿
- 并发标记
- 并发遍历扫描整个对象图(并发),扫描完成后还要修正SATB记录下的在并发时候引用有改动的对象
- 最终标记
- 对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB(原始快照)记录。
- 筛选回收
- 负责更新Region的统计数据,对各个region的回收成本和回收价值排序,根据用户期望的停顿时间制定计划,可以自由选择region回收的数量,将被选择的region的存活对象复制到空的region,然后清除掉整个旧region的空间。(复制清理操作涉及对象的移动必须停止用户线程)
G1优缺点
G1是收集器不在致力于一次性清除垃圾,而是让清除速度赶上对象分配速度的开始
相比cms的优点
G1与CMS相比由于采用整体整理,局部复制的算法,所以不会产生大量的空间碎片
相比cms的缺点
g1占用内存更多(每个region的记忆集占堆内存的20%或以上,cms的话只需要维护一个)
执行负载更大(原始快照需要写后屏障跟踪指针变化)
总结
目前在小内存应用上CMS的表现大概率仍然要会优于G1,而在大内存应用上G1则大多能发挥其优势,这个优劣势的Java堆容量平衡点通常在6GB至8GB之间
region的记忆集占堆内存的20%或以上,cms的话只需要维护一个)
执行负载更大(原始快照需要写后屏障跟踪指针变化)
总结
目前在小内存应用上CMS的表现大概率仍然要会优于G1,而在大内存应用上G1则大多能发挥其优势,这个优劣势的Java堆容量平衡点通常在6GB至8GB之间