java CMS,G1垃圾收集器工作流程原理浅析
JVM内存空间基础知识点(基于JDk1.8)
1.方法区:逻辑概念,元空间,方法区主要用于存储类的信息、常量池、方法数据、方法代码等。方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
2.程序计数器:程序计数器可以看作当前线程所执行的字节码的行号指示器。如果线程执行的是Java方法那么这个计数器记录的是正在执行的虚拟机字节码指令地址。如果执行的是Native方法,这个计数器为空。
2.Java虚拟机栈:Java虚拟机栈跟程序计数器一样是线程私有的,它的生命周期和线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程。
3.本地方法栈:本地方法栈与虚拟机栈作用相似,它们之间的区别是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。
4.Java堆: Java堆通常是Java虚拟机所管理的内存中最大的一块。Java堆是被锁有线程共享的一块内存区域,在虚拟机启动时创建。这块区域唯一的目的就是存放对象实例,几乎所有对象实例都在该区域分配内存。
Java堆时垃圾收集器管理的主要区域(GC堆),从内存回收的角度(收集器一般采用分代收集算法),Java堆还可以细分为:新生代和老年代。新生代再细分有:Eden空间、From Survivor空间、To Survivor空间。
1)简述垃圾收集算法
1.标记-清除算法:
两遍扫描,第一遍扫描枚举GCRoots根节点对象标记出不可回收对象,第二遍扫描找到可回收对象进行清理。算法简单,存活对象较多的情况下效率比较高,但是容易产生一个内存碎片化的问题。
2.复制算法:
一次扫描找到存活对象进行复制,效率较高。适用于存活对象较少的情况,新生代垃圾收集(新生代对象存活率百分之二)。但是对内存要求较高,一半的内存空间预留。
3.标记-整理算法:
两遍扫描找到存活对象并进行移动整理。内存不会造成浪费,但是回收效率较低。
4.分代收集算法:前几种算法的组合
2)垃圾收集器介绍
1.serial系列(标记整理算法)
serial-serialold组合,单线程垃圾收集器,GC线程工作的时候会暂停所有用户线程,产生stop the world。适用于内存较小的内存垃圾回收,简单而高效,不存在多线程交互开销,效率高。一般是硬件能力不足的情况下推动产生的。
2.Parallel系列(标记整理算法)
Parallel 并行线程垃圾收集器,GC线程工作的时候暂停用户线程,产生stop the world。内存较大时,单线程GC工作时造成的stop the world时间过长,影响用户体验。Parallel系列垃圾收集器GC线程并行清理,减少gc工作线程时间。
3.CMS垃圾收集器。重点(垃圾清除算法)
CMS垃圾收集器工作流程
1.初始标记:枚举GCRoots对象,暂停用户线程,扫描对象较少,Stop the world时间短。
2.并发标记:GC线程,用户线程并发工作,耗时时间最久的一部分内容(80%GC时间),GC线程抢占CPU,对用户线程有影响。由于用户线程是并发执行的,用户线程会造成一些对象的引用改变,产生新的垃圾对象。
3.重新标记:短暂的stop the world,不会有对象的状态改变。并发标记产生的新的垃圾会在这阶段被标记出来。
4.并发清理: GC线程和用户线程并发执行,产生的浮动垃圾由下一次垃圾回收清理。
缺点:CMS进行垃圾收集的时候需要预留一定的内存空间给用户线程工作(默认68%,可设置),在内容空间不足时,会导致回收失败,此时会通过serialold进行垃圾回收,速度特别慢。
CMS垃圾收集器采用的是三色标记算法。
由于CMS采用的是并发标记,在GC线程工作时,此时有一个黑色的对象的引用对象被置为null,由于黑色对象不需要再被重新扫描,此时又没有其他引用跟置为null的引用对象产生关联时。就会产生一个浮动垃圾的情况。
在一个灰色对象的引用白色对象引用被置为null,并将黑色对象的属性引用指向该白色对象时,会产生一个漏标现象。CMS采用三色标记法+incremental update算法,在黑色对象引用增加时,将黑色引用对象置为灰色,下次重新扫描。
4.G1垃圾收集器。重点,(region内标记清楚算法,region之间复制算法)
G1逻辑分代概念。基于整个内存划分为四个不同类型的内存区域。
H:存放大对象
每个region都有一个RememberrdSet内存区域 记录了其他region到benregion的引用,垃圾收集器只需扫描该区域即可。
G1分为YGC(新生代回收),FGC(整个堆内存回收),MixedGC(可设置MixedGC启动的内存大小)
MixedGC类似于CMS,在堆内存空间使用超过45%时,产生MixedGC。G1采用三色标记法+SATB算法,在灰色对象的白色引用对象引用清楚时,置入堆栈对象引用,后续扫描RSet枚举根对象判断是有有新的引用指向该白色对象。没有则进行清楚。
CMS垃圾清理流程图 及 具体逻辑
1、新创建的对象一般会被分配在新生代中。常用的新生代的垃圾回收器是 ParNew 垃圾回收器,它按照 8:1:1 将新生代分成 Eden 区,以及两个 survivor 区。创建的对象将 Eden 区全部挤满,这个对象就是「挤满新生代的最后一个对象」。此时,Minor Gc 就触发了。
2、在正式 Minor Gc 前,JVM 会先检查新生代中对象,是比老年代中剩余空间大还是小。Minor Gc 之后 survivor 区不放剩余对象,这些对象就要进入到老年代,所以要提前检查老年代是不是够用。
3、老年代剩余空间如果大于新生代中的对象大小,那就直接 Minor Gc,Gc 完survivor 不够放,老年代够放。
老年代剩余空间如果小于新生代中的对象大小,这时候就要进入老年代空间分配担保规则。
4、老年代空间分配担保规则:如果老年代中剩余空间大小,大于历次 Minor Gc 之后剩余对象的大小,那就允许进行 Minor Gc。因为从概率上来说,以前的放的下,这次的也应该放的下。那就有两种情况:
一:老年代中剩余空间大小,大于历次 Minor Gc 之后剩余对象的大小,进行 Minor Gc
二:老年代中剩余空间大小,小于历次 Minor Gc 之后剩余对象的大小,进行 Ful GC,把老年代空出来再检查。
结合第四步,开启老年代空间分配担保规则只能说是大概率上来说,Minor Gc 剩余后的对象够放到老年代,如果放不下:Minor Gc 后会有这样三种情况:
Minor Gc 之后的对象足够放到 survivor 区,Gc 结束。
Minor Gc 之后的对象不够放到 survivor 区,接着进入到老年代,老年代能放下,那也可以,GC 结束
Minor Gc 之后的对象不够放到 survivor 区,老年代也放不下,那就只能 Full GC。
以上是成功 GC 的例子,以下3 中情况,会导致 GC 失败,报 OOM:
紧接上一节 Ful GC 之后,老年代任然放不下剩余对象,就只能 00M.
未开启老年代分配担保机制,且一次FuI GC 后,老年代任然放不下剩余对象,也只能 OOM。
开启老年代分配担保机制,但是担保不通过,一次Ful GC 后,老年代任然放不下剩余对象,也是能 OOM。
注:
老年代分配担保机制在JDK1.5以及之前版本中默认是关闭的,需要通过HandlePromotionFailure手动指定,JDK1.6之后就默认开启。如果我们生产环境服务使用的是JDK/1.7JDK1.8,所以不用再手动去开启担保机制。
Full GC主要指新生代、老年代、metaspace上的全部GC
使用G1垃圾回收器的例子