代码语言:javascript复制
通过之前的文章我们知道JVM的GC是分代的;不同的区域使用不同的垃圾回收器,使用不同的算法。
新生代: 复制算法 Serial收集器 ParNew收集器 Parallel Scavenge收集器
老年代: 标记-清除-算法 Serila Old收集器 Parallel Old收集器 CMS(Concurrent Mark Sweep)收集器
GC Root
哪些可以作为gc root 1、栈中的引用对象。 如:
代码语言:javascript复制void test() {
B b = new B(); // 引用对象b
}
2、方法区中类静态属性引用的对象。 如:
代码语言:javascript复制public class B {
private static A a; // 类静态属性引用对象
}
3、方法区中常量引用的对象。 如:
代码语言:javascript复制public class B {
private static final A a; // 类静态属性引用对象
}
4、栈中JNI中引用的对象。 如:
代码语言:javascript复制void test() {
JNI引用对象
}
https://blog.csdn.net/tjiyu/article/details/53983650 使用场景
新生代收集器
新生代 Serial收集器
代码语言:javascript复制一个单线程收集器,在进行回收的时候,必须暂停其他所有的工作线程,直到收集结束。
缺点:因为要完全暂停线程,所以用户体验不佳。但是由于新生代回收得较快,所以停顿
的时间非常少,而且没有线程切换的开销,因此也简单高效。通过-XX: UseSerialGC参数启用。
新生代 ParNew收集器
代码语言:javascript复制这个是Serial收集器的多线程版本,适用于多核CPU的设备。但对于单核的设备来说,
需要进行线程之间的切换,效率反而没有单线程的高。通过-XX:ParallelGCThreads
参数限制收集的线程数,-XX: UseParNewGC参数启用。
新生代 Parallel Scavenge收集器
代码语言:javascript复制该收集器关注点和其他的收集器不同,其他的关注点是尽可能的缩短Full GC的时间。
而该收集器关注的是一个可控的吞吐量。吞吐量=运行代码的时间/(运行代码的时间 GC
的时间),通过参数-XX:MaxGCPauseMillis设置最大GC的停顿时间和
-XX:GCTimeRatio 设置吞吐量的大小。
-XX: UseParallelGC参数启用。主要适合在后台运算而不需要太多交互的任务。
老年代收集器
老年代 Serila Old收集器
代码语言:javascript复制该收集器是Serial收集器的老年代版,同样是一个单线程的收集器,优劣势和Serial
收集器一样,这里就不多说了。
老年代 Parallel Old收集器
代码语言:javascript复制是Parallel Scavenge收集器的老年代版本。关注点也和Parallel Scavenge收
集器一样,注重系统的吞吐量,适合于CPU资源敏感的场合。
老年代 CMS(Concurrent Mark Sweep)收集器
代码语言:javascript复制是一种以最短停顿时间为目标的收集器。当应用尤其重视服务的响应速度,希望系统能有
最短的停顿时间,该收集器非常适合。
CMS收集器的收集过程比以往的收集器都要复杂,收集过程分为四个步骤:初始标记、并
发标记、重新标记、并发清除。
初始标记
初始标记会STW,仅仅只是标记GC Roots能够直接关联的对象(并不是死掉的对象~),
由于有OopMap的存在,因此该步骤速度非常快。
并发标记
并发标记不会STW,他和我们的主程序线程共同执行,从上一步被标记的对象开始,进行可达性分析组成“关系网”。由于不需要进行SWT,所以该步骤不会影响用户体验。既然跟我们的主线程共同执行,那么会不会出现在初始标记阶段是可达的但是之后变成不可达(被死亡),会不会出现原本是不可达的,但是后面变成可达(被救活),所以就需要第三步
重新标记
重新标记会STW,重新标记只是为了修改在上一步标记中有了变动的对象。有了这一步,就不怕回收掉不该回收的对象了。而且,由于这一步只是对上一步的结果进行修改,所以STW的时间相当短,对用户的影响不大
最后一步就是并发清除了,这一步也不需要进行STW,只是清除一些不在“关系网”上的对象而已。
讲到这里,大家应该知道了该收集器如何做到最短停顿时间了吧。通过一次短STW时间的
标记和一次不需要STW的标记,大大缩下来第三步标记的范围(只需要修改就好了),第
四步不需要STW。
看上去很完美,但还是有他的缺陷:大量使用了并发操作,因此会占用一部分CPU的资
源,导致吞吐量下降;当在并发清除垃圾的时候,也就是第四步的时候,他是与当前主线
程并发执行的,因此他在回收的时候,我们的主线程又会产生新的垃圾,而这些垃圾在这
次回收过程已经回收不了了,只能等待下一次回收了。这些垃圾又叫做“浮动垃圾”。
CMS是基于“标记-清除”算法实现的收集器,结束时会有大量空间碎片产生。空间碎片过多,可能会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前出发FullGC。
CMS收集器对CPU资源非常敏感。CPU个数少于4个时,CMS对于用户程序的影响就可能变得很大,为了应付这种情况,虚拟机提供了一种称为“增量式并发收集器”的CMS收集器变种。
G1垃圾回收-标记整理算法
代码语言:javascript复制之前讲了新生代和年老代的收集器,在本篇博文中介绍一个收集范围涵盖整个堆的收集器——G1收集器。
先讲讲G1收集器的特点,他也是个多线程的收集器,能够充分利用多个CPU进行工作,收集方式也与CMS收集器类似,因此不会有太久的停顿。
虽然回收的范围是整个堆,但还是有分代回收的回收方式。在年轻代依然采用复制算法;
年老代也同样采用“标记-清除-整理”算法。
但是,新生代与老年代在堆内存中的布局就和以往的收集器有着很大的区别:
G1将整个堆分成了一个个大小相等的独立区域,叫做region。其中依然保存着新生代和年老代的概念,如下图
是不是和之前博文中看到的不同(这是内存空间图,不要和垃圾回收的图弄混了),
以往只是简单的分区域,而这里是将整个堆分成多个大小相等的区域。
他的回收过程也分为四个部分:初始标记、并发标记、最终标记、筛选回收。
大家是不是觉得很熟悉!上面我们也说过了,和CMS收集器类似。
初始标记需要STW;
并发标记不需要;
最终标记就是做一些小修改,需要STW;
而筛选回收则有些不同,在众多的region中,每个region可回收的空间各不相同,但
是回收所消耗的时间是需要控制的,不能太长,因此G1就会筛选出一些可回收空间比较
大的region进行回收,这就是G1的优先回收机制。这也是保证了G1收集器能在有限的
时间内能够获得最高回收效率的原因。
通过-XX:MaxGCPauseMills=50毫秒设置有限的收集时间。
每个region之间的对象引用通过remembered set来维护,每个region都有一个remembered set,
remembered set中包含了引用当前region中对象的指针。
虚拟机正是通过这个remembered set去避免对整个堆进行扫描来确认可回收的对象。
到此,所有的收集器都已经讲完了,但是很重要的一点:每个收集器是不能随意进行组合使用的!这里我列出一个搭配使用的表格提供大家参考使用:
jvm参数设置
from