Parallel Collector
-XX:+UseParallelGC
可以手动开启,如果是server模式,默认开启并行收集器
-XX:ParallelGCThreads=<N>
开启多少个GC线程
默认情况下
CPU>8 N=5/8
CPU<8 N=CPU
Parallel Collector Ergonomics
Ergonomics:自适应,自己调整堆的大小满足设定的要求
-XX:MaxGCPauseMillis=<N> //最大停顿时间
-XX:GCTimeRatio=<N> //吞吐量
-Xmx=<N> //最大堆的大小
给GC设定这些指标,让GC调整堆的大小,满足这些指标,优先满足停顿时间,其次是吞吐量
动态内存调整
针对自适应
generation的增长、减少比率,默认增长比率为20%,缩小比率为4%。
-XX:YoungGenerationSizeIncrement=<Y> young generation增长比率
-XX:TenuredGenerationSizeIncrement=<T> tenured generation增长比率
-XX:AdaptiveSizeDecrementScaleFactor=<D> 缩小因子,如果增长比率是X,那么缩小比率就是 X / D
CMS Collector
并发收集
低停顿、低延迟
老年代收集器
CMS垃圾收集过程
- CMS initial mark :初始化标记Root,STW(需要停止应用程序)
- CMS concurrent mark :并发标记
- CMS-concurrent-preclean:并发预清理
- CMS remark:重新标记 ,STW(在做并发标记、并发清理的时候,我们的应用程序还是在执行的,同时也会做内存的分配,回收,有些新的内存又变成垃圾了,所以要做重新标记,也需要停止应用程序)
- CMS concurrent sweep:并发清理
- CMS concurrent-reset:并发重置
CMS缺点
CPU敏感
当GC执行的时候,应用程序也是执行的,假如是2核cpu,启动一个垃圾回收线程,那么现在只有1个cpu可以响应任何的请求,cpu被占用1半,应用程序只能使用剩下的cpu了
浮动垃圾
在做GC时,应用程序还是在做内存分配的,就会产生浮动垃圾和空间碎片
空间碎片
CMS的相关参数
-XX:ConcGCThreads:并发的GC线程数(与应用程序并发一起执行的线程数)
-XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩,减少垃圾碎片
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次
-XX:CMSInitiatingOccupancyFraction:old区占有填满多少存活对象的时候才会触发full gc(90%)
-XX:+UseCMSInitiatingOccupancyOnly:是否动态调,启用是固定值
-XX:CMSScavengeBeforeRemark:FullGC之前先做YGC
-XX:+CMSClassUnloadingEnabled:启用回收Perm区
G1 Collector
简介
适用于大内存(>=6g),而且停顿时间很小(<0.5秒)
新生代和老生代收集器
G1关键概念
Region
G1默认把堆内存分为1024个分区,后续垃圾收集的单位都是以Region为单位的。Region是实现G1算法的基础,每个Region的大小相等,通过-XX:G1HeapRegionSize参数可以设置Region的大小
E代表是Eden区,S代表Survivor,O代表Old区,H代表humongous表示巨型对象(超过Region空间一半的对象存放在H区)。从图中可以看出各个区域逻辑上并不是连续的。并且一个Region在某一个时刻是Eden,在另一个时刻就可能属于老年代。G1在进行垃圾清理的时候就是将一个Region的对象拷贝到另外一个Region中。
SATB
SATB的全称是Snapchat-At-The_Beginning。SATB是维持并发GC的一种手段。G1并发的基础就是SATB。SATB可以理解成在GC开始之前对堆内存里的对象做一次快照,此时活的对象就认为是活的,从而形成一个对象图。在GC收集的时候,新生代的对象也认为是活的对象,除此之外其他不可达的对象都认为是垃圾对象。
如何找到在GC的过程中分配的对象呢?每个region记录着两个top-at-mark-start(TAMS)指针,分别为prevTAMS和nextTAMS。在TAMS以上的对象就是新分配的,因而被视为隐式marked。通过这种方式我们就找到了在GC过程中新分配的对象,并把这些对象认为是活的对象。
解决了对象在GC过程中分配的问题,那么在GC过程中引用发生变化的问题怎么解决呢, G1给出的解决办法是通过Write Barrier。Write Barrier就是对引用字段进行赋值做了环切。通过Write Barrier就可以了解到哪些引用对象发生了什么样的变化。
RSet
RSet全称是Remember Set,每个Region中都有一个RSet,记录的是其他Region中的对象引用本Region对象的关系(谁引用了我的对象)。
YoungGC
新对象进入Eden区
存活对象拷贝到Survivor区
存活时间达到年龄阈值时,对象晋升到Old区
MixedGC
不是FullGC,回收所有的Young和部分Old
global concurrent marking
global concurrent marking
- initial marking phase :标记GC Root,STW
- Root region scanning phase:标记存活Region
- 标记存活的对象
- Remark phase:重新标记,STW
- Clean phase:部分标记
MixedGC时机
InitiatingHeapOccupancyPercent
堆占有率达到这个数值则触发global concurrent marking,默认是45%
G1HeapWastePercent
在global concurrent marking结束之后,可以针对区有多少空间要被回收,在每次YGC之后和再次发生MixedGC之前,会检查垃圾占比是否达到此参数,只有达到了,下次才会发生Mixed GC
MixecGC相关参数
G1MixedGCLiveThresholdPercent
Old区的region被回收的时候的存活对象占比
G1MixedGCCountTarget
一次global concurrent marking之后,最多执行Mixed GC的次数
G1OldCsetRegionThresholdPercent
一次Mixed GC中能选入CSet的最多old去区的region数量
-XX:+UseG1GC
开启G1
-XX:G1HeapRegionSize=n
Region的大小,1-32M,不得超过2048个Region
-XX:MaxGCPauseMillis=200
最大停顿时间
-XX:G1NewSizePercent
设置要用作年轻代大小最小值的堆百分比
-XX:G1MaxNewSizePercent
设置要用作年轻代大小最大值的堆百分比
-XX:G1ReservePercent
保留防止to space溢出(young区分为eden区和Survivor区,Survivor区分为S0和S1,young gc时,会把其中的一个Survivor区和eden区存活的对象复制到另一个Survivor区,保留该内存,防止拷贝的时候内存不够,默认10%)
-XX:ParallelGCThreads
SWT的线程数
-XX:ConcGCGCThreads
并行线程数=1/4*并行
最佳实践
年轻代大小:避免使用-Xmn、-XX:NewRatio等显示设置Young区大小,会覆盖暂停时间目标
暂停时间目标:暂停时间目标不要太苛刻,其吞吐量目标是90%的应用程序时间和10%的垃圾回收时间,太苛刻会直接影响到吞吐量