年轻代垃圾收集器
Serial收集器【年轻代】
Parnew收集器(主要是可以兼容CMS收集器,1.8默认)【年轻代】
Parallel Scavenge收集器【老年代】
老年代垃圾收集器
Serial Old收集器【老年代】
CMS收集器(1.8默认)【老年代】
Parallel Scavenge垃圾回收器【老年代】
G1、ZGC等...
前言
我们现在的常用的JDK1.8默认是Parellel Scavenge加Parellel Old垃圾收集器配合的使用方式
这种方式在我们现在用的多吗? 如果你是在一个传统软件公司里面那么这种场景是很常见的,但是如果是在一个互联网网企业中JDK默认的垃圾收集器却并不常见。
为什么?互联网公司在设计系统的时候并不像传统软件公司一样,软件公司所面对的人群不多也许就是为一个单位的100来人所制作的,系统所承担的压力并不大,也许在10年的电脑放在这里运行也是没有问题的。但是互联网公司不同,他们所面向的是广大群众,服务器的压力自然就比传统软件公司的系统压力自然要大。
在了解CMS前我们先了解一下Parallel垃圾收集器
正所谓磨刀不误砍柴工,那么为什么Parallel没有被互联网公司所青睐呢?
这张图就是Parallel垃圾清理过程,一目了然在GC启动的时候直接使用了"Stop the world!" 。
为什么JDK选择了这种方式呢?其实并没有什么特别的原因,科技的的发展并不是一跃而起是一点一点兴起的,在JDK1.8的时候中国的网民远远没有现在的那么多,所以JDK选择了这种简单便捷的方式
为什么需要使用到"Stop the world!"?很好理解在多线程序中,如果我GC收集器前脚刚走后脚对象就过期了,那我们运气刚好全是一走就过期那么还怎么清理,后面的对象都过期了GC都没有给发现导致整体对象都存活了下来这是不是就有大问题
但是这种"Stop the world!"的方式对用户非常不友好。我现在正在付款,后面那么多的人等我,好了现在服务器Eden园区满了启动Minor GC了一运行那就好几秒钟,再加上网络延迟那不尴尬的扣出了三室一厅。
CMS垃圾收集器
为了解决原先stop the world过长问题
CMS 全名为 Concurrent Mark Sweep ,它的出现就是为了解决在Parallel垃圾回收器中STW(Stop the world)等待时间过长的问题,我们来看一张图
我来一一解释一下每个阶段的作用
需要拥有基础:节点三色标记算法【如果不知道的话前往主页查询我发布的:三色标记算法原来如此简单!】
- 首先是应用程序进来,触发垃圾回收器
- 初始标记:这个时候就是标记GC Root(存活对象的根节点)【注意这里会Stop the world,因为这里只是标记GC Root所以比较快, 用户基本没有感觉】
- 并发标记:根据根节点向下寻找对应的可以访问到的对象,将可以访问到的对象标记为黑色或者灰色,如果对象中的所有属性均已经赋值的话那么就将这个节点标记为黑色,如果对象中的属性并没有完全赋值的话那么就变为灰色
- 重新标记;这里就是去匹配一下并发标记中是否有对象属性的赋值,查看灰色和黑色。再去匹配一次GC Root根节点是否有重新赋值、或者是为没有赋值过的变量赋值(我记得它会将这个时候的赋值给记录下来,以此来减少全盘扫描).【注意这里会Stop the world,这里依然只是标记,用户感知不多】
- 并发清理:清理掉没有被GC标记的对象(也就是没有被引用的对象)
- 并发重置:重置GC标记
其实这里的操作是很复杂的,时间肯定要比原来的直接STW要慢,但是这只是对于服务器,这个垃圾回收器耗时的删除以及遍历有效节点使用了并发处理。CMS这个机制对用户的体验是很好的。
在我们实际工作中使用CMS还会配合一个参数: -XX:+UseCMSCompactAtFullCollection(执行几次CMS垃圾回收器【Full GC】)后对内存进行碎片化整理。
结果
最后:大家思考一个问题?
垃圾回收是并发处理的,那么如果在我并发执行垃圾清除的时候如果Eden又满了,又需要调用一次Minor GC这个时候会怎样。
很简单:原来的垃圾回收不做了,直接使用"Stop the world",然后使用Serial Old(效率极低,单线程)来执行垃圾回收
如果解决:
一般就是对老年代添加一个阈值,并不要等到老年代满了才执行Full Gc,我们可以让它在指定的容量下就执行比如说92%,但是JVM也不傻它也92%就是它的默认值。如果有特殊情况我们就手动设置就可以了。