JVM垃圾收集器

前言

在看下面内容之前,得先了解两个东西,GC ROOTS和可达性分析算法。GC ROOTS:必须是一组活跃的引用。这玩意儿不大好写,网上可以搜
一大堆说明。我想的就是可达性分析算法的起点。可达性分析算法:通过gc roots去遍历它的所有引用对象。再去通过引用对象去遍历引用对象
的引用对象。以此类推。如此一来在可达性分析算法结束后,所有与GC ROOTS 有直接关联或者间接关联的对象就会形成一条引用链,代表可达
而那些没有任何关联的对象就代表不可达。

垃圾收集算法

分代收集理论

根据我们对象存活的周期不同,将内存划分为一块一块的,一般将堆分为新生代(eden、s0、s1)、老年代。这样可以针对每个区域去使用适合
的垃圾收集算法。

标记复制算法

将内存划分为相等两块 A B,其中A块使用完的时候就将还存活的对象复制到B块内存区域中,然后直接清理A块的内存。

标记-清除算法

标记存活的对象,然后回收没有标记的对象,也可以反过来,标记回收的对象然后统一回收。
此方法有两个缺点。1:效率问题(标记对象太多的话效率就高)2:空间问题(每次回收后会产生不连续的空间碎片)

标记-整理算法

与上方标记方法一致,变化的是回收的时候先将所有存活的对象向内存的一端移动,然后从端边开始清理剩下的内存

垃圾收集器

垃圾收集算法是内存回收的方法,那么垃圾收集器就是方法具体实现。目前没有一款适用于任何场景的万能垃圾收集器,所以我们需要根据业务
场景去设置不同的拉机器这里需要解释一个名词 gcroots。什么是gcroots:虚拟机栈中局部变量(也叫局部变量表)中引用的对象、方法区
中类的静态变量、常量引用的对象、本地方法栈中 JNI (Native方法)引用的对象 

Serial收集器

垃圾收集器设置参数
-XX:+UseSerialGC         新生代(标记-复制算法)
-XX:+UseSerialOldGC      老年代(标记-整理算法)
该款垃圾收集器是最基本的一款垃圾收集器,它采用“单线程”模式收集垃圾,而且在收集垃圾的过程中会停止所有用户线程的工作
(Stop The World),直至垃圾手机完毕。

serial垃圾收集器回收过程

Parallel Scavenge收集器

垃圾收集器设置参数
-XX:+UseParallelGC       新生代 标记-复制算法
-XX:+UseParallelOldGC    老年代 标记-整理算法
Parallel收集器其实就是Serial收集器的多线程版本。默认收集的线程数与CPU核数相等。

ParNew

垃圾收集器设置参数
-XX:+UseParNewGC  标记-复制算法
该收集器专门针对新生代,跟Paranllel很相似。主要是他和CMS(老年代)收集器配合使用

CMS收集器(Concurrent Mark Sweep)

CMS收集器流程图

CMS垃圾收集器启用

-XX:+UseConcMarkSweepGC  老年代 标记-清除算法(三色标记算法)
该垃圾收集器是一种获取最短回收停顿时间时间为目标的垃圾收集器,也是一款并发收集器,可以做到垃圾回收线程与用户线程(基本上)同时
工作。上图为大家说明为什么是基本上同时工作

回收过程

初始标记:
标记所有gcroots对象。gcroots就是我们内存中局部变量、常量、静态变量这些东西,包括但不限于。
并发标记
从标记的gcroots的对象开始延伸,遍历整个对象图。这种情况会耗时比较长,但是从上图也可以看出他是和用户线程并行的,所以并不影响
用户的体验。并发标记问题。因为是和用户线程并行,所以可能会出现你标记的时候是垃圾,但是用户线程正在运行阶段这个对象突然变成
不是垃圾了(这就是漏标)。
重新标记
这个阶段就是为了解决并发标记出现的漏标的问题。它会修正因为用户线程进来导致变动那一部分对象。这个阶段会比并发标记的时间短很多。
并发清理
回收白色对象(下面三色标记说明白色对象)
并发重置
重置本地GC的标记对象

CMS的相关核心参数

-XX:+UseConcMarkSweepGC 启用cms 
-XX:ConcGCThreads=8 并发的GC线程数,建议与处理器核数相等
-XX:+UseCMSCompactAtFullCollection  FullGC之后做压缩整理(减少碎片)-建议开启
-XX:CMSFullGCsBeforeCompaction= 1 多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次  
-XX:CMSInitiatingOccupancyFraction=80: 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
-XX:+UseCMSInitiatingOccupancyOnly 只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定
JVM仅在第一次使用设定值,后续则会自动调整
-XX:+CMSScavengeBeforeRemark 在CMS GC前启动一次minor gc,降低CMS GC标记阶段(也会对年轻代一起做标记,如果在minor gc
就干掉了很多对垃圾对象,标记阶段就会减少一些标记时间)时的开销,一般CMS的GC耗时 80%都在标记阶段
-XX:+CMSParallellnitialMarkEnabled 表示在初始标记的时候多线程执行,缩短STW
-XX:+CMSParallelRemarkEnabled 在重新标记的时候多线程执行,缩短STW;

三色标记

黑(非垃圾对象)
代表这个对象中的所有直接引用对象都被扫描过了,那么这个对象就会被标记成黑色,在重新标记过程中,黑色对象是不会再被扫描了,比如A
对象里面只有b,c两个成员变量,他们两个的引用的对象都被扫描过了,那么A就会变成黑色。如果从其他gc roots触发通过一些列引用链又指
向了A那么到A这里就会结束当前引用链的扫描。
灰(非垃圾对象)
代表这个对象的直接引用对象至少还有1个没有被扫描。就比如我正标记B这个对象,B对象里面有c,d两个对象,在扫描c对象时B对象的标记
就是灰色,因为还有d对象没有扫描。
白(垃圾对象)
代表从没有被垃圾收集器访问过。在可达性分析算法结束后如果对象还是白色,那么代表这个对象没有任何引用。可以被删除
在CMS回收垃圾的阶段。由于用户线程还在运行,这样会导致有些对象本来是垃圾但又突然变得不是垃圾了或者不是垃圾的又变成垃圾了。
比如:
三色标记问题:
多标and漏标
1.多标情况-在标记的过程中这个对象标记成了不是垃圾。但是在回收过程中随着用户线程的结束它又变成了垃圾对象,但是CMS已经将它标记
成了不是垃圾对象。这种是多标不存在什么大问题,只是浮动垃圾而已,下次gc清理就行了。
2.漏标情况-这个就存在大问题。本来在系统中存在的垃圾对象且CMS已经将它标记成了垃圾对象。但是随着用户线程正在运行它又变成不是垃
圾了,比如:A是黑色对象,D是白色对象。但是在运行过程中,A突然有个引用指向D。如果这个时候CMS将它回收那么我们系统就没法运行了。
用户线程中的变量存的内存指针结果指过去什么都没有还怎么运行程序。
增量更新-处理漏标问题
增量更新:就是当黑色对象重新有指向白色,会将这个新的引用记录下来。在并发标记结束后会将这些对象取出来找到引用它的黑色对象再进行
一次扫描。可以理解成在重新标记之前将这个黑色对象变成灰色对象。在重新标记的时候再进行扫描。

G1垃圾收集器

G1垃圾收集器启用与说明

-XX:+UseG1GC 使用G1收集器
G1垃圾收集器将不再存在上面垃圾收集器中的 年轻代 老年代的绝对内存划分。虽说也是会划分这些区域,但是是间隔没有连在一起,而且
都是会变动可能一开始是老年代,经过GC后变成年轻代了。
G1垃圾收集器是将堆平均分成2048个region,可以理解成格子。里面包含四种4种区域,eden、survivor、old、humongos。
eden:还是我们的年轻代。JVM会不断给年轻代分配多的格子,但是最多不会超过整个堆的60%
survivor:理解成我们上面堆中年轻代 s0、s1区域。默认格子比例还是和上面一样8:1:1
old:老年代
humongos:大对象区域,就是当一个对象超过一个region的50%的大小就会被放在humongos中,如果很大可以夸多个humongos存放。

G1垃圾收集器其他参数

-XX:G1MaxNewSizePercent 年轻代分配占比,50 代表50%
-XX:ParallelGCThreads=8 指定GC工作的线程数量
-XX:G1HeapRegionSize=8 指定分区大小(1MB~32MB,且必须是2的N次幂),默认将整堆划分为2048个分区
-XX:MaxGCPauseMillis=200 目标暂停时间(默认200ms) 
-XX:G1NewSizePercent=5 新生代内存初始空间(默认整堆5%,值配置整数,默认就是百分比)-XX:G1MaxNewSizePercent:新生代内存
最大空间
-XX:TargetSurvivorRatio=50  Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个年龄对象)
总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代
-XX:MaxTenuringThreshold=15 最大年龄阈值(默认15)
-XX:InitiatingHeapOccupancyPercent=45 老年代占用空间达到整堆内存阈值(默认45%),则执行新生代和老年代的混合收集
(MixedGC)比如我们之前说的堆默认有2048个region,如果有接近1000个region都是老年代的region,则可能就要触发MixedGC了
-XX:G1MixedGCLiveThresholdPercent=85 (默认85%)  region中的存活对象低于这个值时才会回收该region,如果超过这个值,
存活对象过多,回收的的意义不大。
-XX:G1MixedGCCountTarget=8 在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一会,然后暂停回收
恢复系统运行,一会再开始回收,这样可以让系统不至于单次停顿时间过长。
-XX:G1HeapWastePercent=5 (默认5%): gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都是基于复制算法
进行的都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清理掉,这样的话在回收过程就会不断
空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立即停止混合回收,意味着本次混合回收就结束了

在这里插入图片描述

G1(Garbage-First)回收过程

初始标记
同CMS的初始标记一致
并发标记
同CMS的并发标记一致
最终标记
同CMS的重新标记一致,不过有一点不同,CMS重新标记采用的是“增量更新”G1采用的“原始快照(SATB)”。原始快照也是解决漏标的情况。
比如当灰色对象引用的白色对象之间的引用链断掉之后,就会记录下这个白色对象,将白色对象变成灰色,这样就不会被回收,等待下一轮GC
再重新开始标记。这样会导致多出一些浮动垃圾。问题不大。
筛选回收
在g1后台会维护一个优先列表,每次回收的时候会根据设置的gc停顿时间(-XX:MaxGCPauseMillis)来制定回收计划,优先回收价值大的
region。这里与cms的并发清理不同,筛选回收会stw。因为时间是可控的所以设置一个不易察觉的停顿时间让jvm专注回收垃圾效率会更高。
回收算法采用的复制算法。将存活的region复制到另一个region中这样不会产生较多的内存碎片,这样就不用像CMS一样,回收完后还要整理
一下内存。g1每次是根据设置的停顿时间来进行回收垃圾,所以极有可能回不完垃圾。等待下一轮gc再进行回收。
G1垃圾收集分类
YoungGC
YoungGC并不是说现有的Eden区放满了就会马上触发,G1会计算下现在Eden区回收大概要多久时间,如果回收时间远远小于参数
 -XX:MaxGCPauseMills 设定的值,那么增加年轻代的region,继续给新对象存放,不会马上做Young GC,直到下一次Eden区放满,
 G1计算回收时间接近参数 -XX:MaxGCPauseMills 设定的值,那么就会触发Young GC
MixedGC
不是FullGC,老年代的堆占有率达到参数(-XX:InitiatingHeapOccupancyPercent)设定的值则触发,回收所有的Young和部分Old
(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及大对象区,正常情况G1的垃圾收集是先做MixedGC,主要使用复制算法,需要
把各个region中存活的对象拷贝到别的region里去,拷贝过程中如果发现没有足够的空region能够承载拷贝对象就会触发一次Full GC
Full GC
停止系统程序,然后采用单线程进行标记、清理和压缩整理,好空闲出来一批Region来供下一次MixedGC使用,这个过程是非常耗时的。
(Shenandoah优化成多线程收集了)

如有错误或者不理解的地方欢迎评论区留言

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值