G1
G1日志详解
[GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0015790 secs]
//young -> 年轻代 Evacuation-> 复制存活对象
//initial-mark 混合回收的阶段,这里是YGC混合老年代回收
[Parallel Time: 1.5 ms, GC Workers: 1] //一个GC线程
[GC Worker Start (ms): 92635.7]
[Ext Root Scanning (ms): 1.1]
[Update RS (ms): 0.0]
[Processed Buffers: 1]
[Scan RS (ms): 0.0]
[Code Root Scanning (ms): 0.0]
[Object Copy (ms): 0.1]
[Termination (ms): 0.0]
[Termination Attempts: 1]
[GC Worker Other (ms): 0.0]
[GC Worker Total (ms): 1.2]
[GC Worker End (ms): 92636.9]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.0 ms]
[Other: 0.1 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.0 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.0 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
//以下是混合回收其他阶段
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0000078 secs]
[GC concurrent-mark-start]
//无法evacuation,进行FGC
[Full GC (Allocation Failure) 18M->18M(20M), 0.0719656 secs]
[Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)], [Metaspace: 38
76K->3876K(1056768K)] [Times: user=0.07 sys=0.00, real=0.07 secs]
吞吐降低了10%-15%,但是它的响应时间能达到200ms及以内。
分层和分而治之。
Humongous:放大对象区域。
old:
Survivor:
Eden:
G1特点:
- 并发收集
- 压缩空闲空间不会延长GC的暂停时间
- 更易预测的GC暂停时间
- 使用不需要事先很高的吞吐量,对响应时间要求很高的场景。
基本概念:
card table:Card Table 由于做YGC时,需要扫描整个Old区,效率非常低,所以JVM设计了CardTable, 如果一个OLD区CardTable中有对象指向Y区,就将它设为Dirty,下次扫描时,只需要扫描Dirty Card 在结构上,Card Table用BitMap来实现
判断新生代对象是否活着的时候(引用已经到老年代了):是不是要遍历老年代(这样效率太扯了)?
BitMap表
CardTbale
JVM GC可以分为MinorGC、MajorGC和FullGC,对于Mirnor GC来讲它的耗时主要由两个因素决定:
- 复制活跃对象的时间
- 扫描card table(老年代对象引用新生代对象)的时间
Java虚拟机用了一个叫做CardTable(卡表)的数据结构来标记老年代的某一块内存区域中的对象是否持有新生代对象的引用,卡表的数量取决于老年代的大小和每张卡对应的内存大小,每张卡在卡表中对应一个比特位,当老年代中的某个对象持有了新生代对象的引用时,JVM就把这个对象对应的Card所在的位置标记为dirty(bit位设置为1),这样在Minor GC时就不用扫描整个老年代,而是扫描Card为Dirty对应的那些内存区域。
Collection Set:那些被标记的card 统计为Collection Set。
RSet=RememeberedSet:
- 记录了其他Region中的对象到本Region的引用。
- Rset的截止在于使得垃圾收集器不需要扫描整个堆找到谁引用了当前分区中的对象,只需要扫描RSet即可。
新老年代比例:动态的
- 5%-60%
- 一般不用手工指定
- 也不要手工指定 ,因为这是G1预测停顿时间的基准。
- 吞吐降低了10%-15%,但是它的响应时间能达到200ms及。
GC何时触发(注意G1还是会FGC,在分配对象特别快的时候)
- YGC
- Eden空间不足
- 多线程并行执行
- FGC
- Old空间不足
- System.gc()
G1中的MixedGC
- 相当于一个CMS
- XX:InitiatingHeapOccupacyPercent
- 默认值超过45%(可以指定,占用堆内存空间)
- 当O超过这个值时,启动MixedGC
MixedGC的过程
- 初始标记STW
- 并发标记
- 最终标记STW(重新标记)
- 筛选回收STW(并行)
三色标记:
- 白色:未被标记的对象
- 灰色:自身被标记,成员变量未被标记
- 黑色:自身和成员变量(引用的对象)均已标记完成
漏标条件:(两个都满足)打破一个即破坏
- 在remark过程中,黑色指向了白色,如果不对黑色重新扫描,则会漏标,会把白色D对象当做没有新引用指向从而回收掉。
- 并发标记过程中,Mutator删除了所有从灰色到白色的引用,会产生漏标,此时白色独享应该被回收。
打破上述两个条件之一即可:
- incremental update --增量更新,关注引用的增加,把黑色重新标记为灰色,下次重新扫描属性(CMS使用)
- SATB snapshot at the beginning -关注引用的删除 当B-->D消失时,要把这个引用推到GC的堆栈,保证D还能被GC扫描到。(G1使用)
为什么G1要用SATB?
- 如果用第一种就会把黑色的引用又重新扫描一遍,效率很低
- 灰色--》白色 引用消失时,如果没有黑色指向白色引用会被push到堆栈(当我下一 次扫描的线程的时候会去扫描堆栈里面哪些发生了改变,只用扫描引用指向哪个白色对象),下次扫描时拿到这个应用,由于有RememeberedSet的存在,不需要扫描整个堆去查找指向白色的引用,效率比较高,SATB配合RSet,浑然天成。(这里注意增量更新不能和RSet配合,不然还是得重新遍历一遍) GC里面的写屏障和内存的写屏障有区别
RSet与赋值的效率
- 由于RSet的存在,那么每次给对象赋引用的时候,就得做一些额外的操作
- 指的是RSet中做一些额外的记录(在GC中被称为写屏障)
- 这个写屏障不等于内存屏障
- No Silver Bullet
颜色指针:(64位)
作业
-
-XX:MaxTenuringThreshold控制的是什么? A: 对象升入老年代的年龄 B: 老年代触发FGC时的内存垃圾比例
-
生产环境中,倾向于将最大堆内存和最小堆内存设置为:(为什么?) A: 相同 B:不同
-
JDK1.8默认的垃圾回收器是: A: ParNew + CMS B: G1 C: PS + ParallelOld D: 以上都不是
-
什么是响应时间优先?
-
什么是吞吐量优先?
-
ParNew和PS的区别是什么?
-
ParNew和ParallelOld的区别是什么?(年代不同,算法不同)
-
长时间计算的场景应该选择:A:停顿时间 B: 吞吐量
-
大规模电商网站应该选择:A:停顿时间 B: 吞吐量
-
HotSpot的垃圾收集器最常用有哪些?
-
常见的HotSpot垃圾收集器组合有哪些?
-
JDK1.7 1.8 1.9的默认垃圾回收器是什么?如何查看?
-
所谓调优,到底是在调什么?
-
如果采用PS + ParrallelOld组合,怎么做才能让系统基本不产生FGC
-
如果采用ParNew + CMS组合,怎样做才能够让系统基本不产生FGC
1.加大JVM内存
2.加大Young的比例
3.提高Y-O的年龄
4.提高S区比例
5.避免代码内存泄漏
-
G1是否分代?G1垃圾回收器会产生FGC吗?
-
如果G1产生FGC,你应该做什么?
- 扩内存
- 提高CPU性能(回收的快,业务逻辑产生对象的速度固定,垃圾回收越快,内存空间越大)
- 降低MixedGC触发的阈值,让MixedGC提早发生(默认是45%)
-
问:生产环境中能够随随便便的dump吗? 小堆影响不大,大堆会有服务暂停或卡顿(加live可以缓解),dump前会有FGC
-
问:常见的OOM问题有哪些? 栈 堆 MethodArea 直接内存
GC常用参数
- -Xmn -Xms -Xmx -Xss 年轻代 最小堆 最大堆 栈空间
- -XX:+UseTLAB 使用TLAB,默认打开
- -XX:+PrintTLAB 打印TLAB的使用情况
- -XX:TLABSize 设置TLAB大小
- -XX:+DisableExplictGC System.gc()不管用 ,FGC
- -XX:+PrintGC
- -XX:+PrintGCDetails
- -XX:+PrintHeapAtGC
- -XX:+PrintGCTimeStamps
- -XX:+PrintGCApplicationConcurrentTime (低) 打印应用程序时间
- -XX:+PrintGCApplicationStoppedTime (低) 打印暂停时长
- -XX:+PrintReferenceGC (重要性低) 记录回收了多少种不同引用类型的引用
- -verbose:class 类加载详细过程
- -XX:+PrintVMOptions
- -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial 必须会用
- -Xloggc:opt/log/gc.log
- -XX:MaxTenuringThreshold 升代年龄,最大值15
- 锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 ... 这些不建议设置
Parallel常用参数
- -XX:SurvivorRatio
- -XX:PreTenureSizeThreshold 大对象到底多大
- -XX:MaxTenuringThreshold
- -XX:+ParallelGCThreads 并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同
- -XX:+UseAdaptiveSizePolicy 自动选择各区大小比例
CMS常用参数
- -XX:+UseConcMarkSweepGC
- -XX:ParallelCMSThreads CMS线程数量
- -XX:CMSInitiatingOccupancyFraction 使用多少比例的老年代后开始CMS收集,默认是68%(近似值),如果频繁发生SerialOld卡顿,应该调小,(频繁CMS回收)
- -XX:+UseCMSCompactAtFullCollection 在FGC时进行压缩
- -XX:CMSFullGCsBeforeCompaction 多少次FGC之后进行压缩
- -XX:+CMSClassUnloadingEnabled
- -XX:CMSInitiatingPermOccupancyFraction 达到什么比例时进行Perm回收
- GCTimeRatio 设置GC时间占用程序运行时间的百分比
- -XX:MaxGCPauseMillis 停顿时间,是一个建议时间,GC会尝试用各种手段达到这个时间,比如减小年轻代
G1常用参数
- -XX:+UseG1GC
- -XX:MaxGCPauseMillis 建议值,G1会尝试调整Young区的块数来达到这个值
- -XX:GCPauseIntervalMillisGC的暂停间隔目标
- -XX:+G1HeapRegionSize 分区大小,建议逐渐增大该值,1 2 4 8 16 32。 随着size增加,垃圾的存活时间更长,GC间隔更长,但每次GC的时间也会更长 ZGC做了改进(动态区块大小)
- G1NewSizePercent 新生代最小比例,默认为5%
- G1MaxNewSizePercent 新生代最大比例,默认为60%
- GCTimeRatio GC时间建议比例,G1会根据这个值调整堆空间
- ConcGCThreads 线程数量
- InitiatingHeapOccupancyPercent 启动G1的堆空间占用比例