1、Serial收集器和Serial Old收集器
运行过程:
Serial收集器是最基础、历史最悠久的收集器。这是一个单线程工作的收集器。“单线程”的意义并不仅仅是说明它会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调它在垃圾收集时,必须暂停其他所有工作线程,直到它收集结束(STW)。
串行收集器新生代采用复制算法,老年代使用标记-整理算法。
优点
优点是简单,对于单CPU,由于没有多线程的交互开销,可能更高效。是默认的Client模式(桌面应用)下的新生代收集器。
缺点
停顿时间较长。
使用下面的命令来开启Serial + Serial Old 的收集器组合
-XX:+UseSerialGC
2、ParNew收集器和CMS收集器
运行过程:
ParNew收集器实质上是Serial收集器的多线程并行版本。
它在垃圾收集时,同样会暂停其他所有工作线程,直到它收集结束(STW)。
并行收集器新生代采用复制算法,老年代采用CMS收集器的算法。
优点
在并发能力好的CPU环境里,它停顿的时间要比串行收集器短。是默认的Server模式下首选的新生代收集器,能够和CMS收集器配合使用。
缺点
但对于单cpu或并发能力较弱的cpu,由于多线程的交互开销,可能比串行收集器更差。
ParNew收集器是激活CMS后(-XX:+UseConcMarkSweepGC)的默认新生代收集器,也可以
使用下面的命令来强制开启ParNew收集器
-XX:+UseParNewGC
3、Parallel Scavenge收集器 和 Parallel Old收集器
运行过程:
新生代Parallel Scavenge收集器/Parallel Old收集器 是一个应用于新生代的、使用复制算法的并行收集器。
跟ParNew很类似、但更关注吞吐量,能最高效率的利用cpu,适合运行后台应用。
优点
能最高效率的利用cpu,适合运行后台应用。
缺点
原本10s收集一次, 每次停顿100ms, 设置完参数之后可能变成5s收集一次, 每次停顿70ms. 停顿时间变短, 但收集次数变多。
使用下面的命令来开启Parallel Scavenge收集器
-XX:+UseParallelGC
使用下面的命令来设置Parallel Scavenge收集器的最大停顿时间
-XX:MaxGCPauseMillis:1000毫秒
使用下面的命令来开启Parallel Scavenge + Parallel Old的收集器组合
-XX:+UseParallelOldGC
4、CMS收集器
运行过程:
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
CMS收集器是基于 标记-清除算法 实现的,它的运作过程相对于前面几种收集起来说要更复杂一些,整个过程分为四步:1.初始标记 2.并发标记 3.重新标记 4.并发清除。
其中初始标记、重新标记两个步骤仍然需要STW。
初始标记:仅仅标记一下GC Roots能直接关联到的对象,速度很快。
并发标记:从GC Roots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。
重新标记:为了修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间段。
并发清除:清理删掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
优点
停顿较低
缺点
对处理器资源非常敏感。
由于是标记清除算法,会产生大量连续的内存碎片。
使用如下命令开启CMS收集器
-XX:+UseConcMarkSweepGC
5、Garbage First收集器(G1收集器)
运行过程:
G1收集器是垃圾收集器技术发展历史上的里程碑式的成果,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式。G1收集器的定位是替换掉CMS收集器。
G1不再坚持固定大小以及固定数量的分带区域划分,而是把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间或者是老年代空间。收集器能够堆扮演不同角色的Region采用不同的策略去处理,这样无论是新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。
Region中还有一类特殊的Humongous区域,专门用来存储大对象。G1认为只要大小超过了一个Region容量一般的对象即可判定为大对象。每个Region的大小可以通过参数【-XX:G1HeapRegionSize】设定,取值范围为1MB~32MB,应该为2的N次幂。对于那些超过了整个Region容量的超级大对象,将会被存放在N个连续的Humongous Region中,G1的绝大多数行为都把Humongous Region作为老年代的一部分来进行看待。
G1收集器是基于标记-整理算法实现的,它的回收过程跟CMS类似,也分为四个步骤:初始标记、并发标记、最终标记、筛选回收。
初始标记、最终标记、筛选回收三个步骤需要STW。
初始标记:仅仅是标记一下GC Roots能直接关联到的对象。
并发标记:从GC Root开始对堆对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,可以与用户线程并发执行。
最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象。
筛选回收:对Region的回收价值进行排序,根据用户所期望的停顿时间来制定回收计划,此阶段必须暂停用户线程。
优点
G1能充分利用多cpu、多核环境硬件优势,尽量缩短STW。
G1整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片。
G1的停顿可预测,能明确指定在一个时间段内,消耗在垃圾收集上的时间不能超过多长时间。
G1跟踪各个Region里面垃圾堆的价值大小(哪些块里面的垃圾最多),在后台维护一个优先列表,每次根据允许的时间来回收价值最大的区域,从而保证在有限时间内的高效收集。
Mixed GC 模式,通常不会进行Full GC,只会进行 新生代 + 价值最大的老年代 的区域回收。
缺点
G1 需要记忆集 (具体来说是卡表)来记录新生代和老年代之间的引用关系,这种数据结构在 G1 中需要占用大量的内存,可能达到整个堆内存容量的 20% 甚至更多。而且 G1 中维护记忆集的成本较高,带来了更高的执行负载,影响效率。
G1收集器参数设置
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定GC工作的线程数量
-XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的N次幂),默认将整堆划分为2048个分区
-XX:MaxGCPauseMillis:目标暂停时间(默认200ms)
-XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%)
-XX:G1MaxNewSizePercent:新生代内存最大空间
-XX:TargetSurvivorRatio:Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代
-XX:MaxTenuringThreshold:最大年龄阈值(默认15)
-XX:InitiatingHeapOccupancyPercent:老年代占用空间达到整堆内存阈值(默认45%),则执行新生代和老年代的混合收集(MixedGC),比如我们之前说的堆默认有2048个region,如果有接近1000个region都是老年代的region,则可能就要触发MixedGC了
-XX:G1MixedGCLiveThresholdPercent(默认85%) region中的存活对象低于这个值时才会回收该region,如果超过这个值,存活对象过多,回收的的意义不大。
-XX:G1MixedGCCountTarget:在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一会,然后暂停回收,恢复系统运行,一会再开始回收,这样可以让系统不至于单次停顿时间过长。
-XX:G1HeapWastePercent(默认5%): gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立即停止混合回收,意味着本次混合回收就结束了。
G1垃圾收集器优化建议
假设参数 -XX:MaxGCPauseMills 设置的值很大,导致系统运行很久,年轻代可能都占用了堆内存的60%了,此时才触发年轻代gc。
那么存活下来的对象可能就会很多,此时就会导致Survivor区域放不下那么多的对象,就会进入老年代中。
或者是你年轻代gc过后,存活下来的对象过多,导致进入Survivor区域后触发了动态年龄判定规则,达到了Survivor区域的50%,也会快速导致一些对象进入老年代中。
所以这里核心还是在于调节 -XX:MaxGCPauseMills 这个参数的值,在保证他的年轻代gc别太频繁的同时,还得考虑每次gc过后的存活对象有多少,避免存活对象太多快速进入老年代,频繁触发mixed gc.
什么场景适合使用G1
1. 50%以上的堆被存活对象占用。
2. 对象分配和晋升的速度变化非常大。
3. 垃圾回收时间特别长,超过1秒。
4. 8GB以上的堆内存(建议值)。
5. 停顿时间是500ms以内。
6、GC的种类
针对HotSpot VM的实现,它里面的GC分为两种
局部GC
Young GC:只收集新生代的GC,还有一种叫法叫做Minor GC。
Old GC:只收集老年代的GC,只有CMS收集器有这个模式。
Mixed GC:收集整个新生代以及部分老年代的GC,只有G1收集器有这个模式。
全局GC
Full GC:收集整个堆,包括新生代、老年代、元空间等所有部分。
7、GC性能指标
吞吐量:应用代码执行的时间 / 运行的总时间(代码执行的时间+ GC执行的时间)
GC负荷:与吞吐量相反,是 GC时间 / 运行的总时间
暂停时间:就是发生STW的时间
GC频率:就是GC在一个时间段发生的次数
反应速度:从对象成为垃圾到被回收的时间
8、JVM内存配置原则
内存比例
新生代 : 老年代 = 1 : 3
Eden : From Survivor : To Survivor = 8 : 1 : 1
新生代会出现的问题
新生代尽可能设置大点儿,如果太小会导致
YGC次数更加频繁
可能导致YGC后的对象进入老年代,如果此时老年代满了,会触发FGC
老年代会出现的问题
对老年代,针对响应时间优先的应用:由于老年代通常采用并发收集器,因此其大小要综合考虑并发量和并发持续时间等参数。
如果设置小了,可能会导致内存碎片,膏回收频率会导致应用长时间暂停。
如果设置大了,会需要较长的回收时间。
优化原则
依据对象的存活周期进行分类,对象优先在新生代分配,长时间存活的对象进入老年代
根据不同代的特点,选取合适的收集算法:少量对象存活,适合复制算法;大量对象存活,适合标记清除或者标记整理。