引言
我们都知道在java编程中,我们不需要手动的释放内存,因为java有着自动的垃圾回收机制,那么在java中什么是垃圾,垃圾回收机制又是怎么回事,本文将会对JVM的垃圾回收机制做个详细的介绍。
自动垃圾回收与手动垃圾回收
自动垃圾回收会使得开发效率提高,但在执行中要进行垃圾回收,所以执行效率会降低,而手动处理垃圾则相反,自动垃圾回收会使得编程相对于手动更加容易。
什么是垃圾
在java中,我们称没有任何引用指向的对象或多个对象(循环引用)为垃圾,这些对象所占据的内存空间需要垃圾回收机制去自动的回收该部分内存。
怎么找到垃圾
1.引用计数器(ReferenceCount)
有引用指向对象,则计数器值+1,当引用失效,则-1;
2.根可达性算法(RootSearching)
以某些对象为根,依次向下搜索,形成条条链,当某个对象没有在链上时,说明该对象从根不可达 即为垃圾。
注:常作根对象: 线程栈变量、静态变量、常量池、JNI指针(本地方法栈指针)
常见的垃圾回收算法
1.标记清除(mark sweep)-进行两遍扫描,先找出垃圾然后进行清除,由于垃圾存在不同的位置,会使得内存产生碎片
2.拷贝算法(copying)-将内存分为两半,当需要GC时,将有用对象复制到 另一半内存,清除这一半,该算法有个弊端就是浪费内存空间。
3.标记压缩(mark compact)-相当于标记清除的改进版,会进行碎片整理,这也表明其效率偏低。
注:一般情况下,JVM会将内存分代,不同的代采用不同的垃圾回收器 不同垃圾回收算法进行分代垃圾回收,我们也常称这种算法为分代垃圾回收算法
内存分代模型
为了提升垃圾回收的效率,JVM采用了内存分代模型,将内存分为了不同的代,新生代(Eden+2个survivor区)、老年代、永久代(MethodArea),不同的代采用不同的垃圾回收器 不同的垃圾回收算法。
- MinorGC= YGC 新生代的回收
MajorGC= FGC 老年代的回收 - 新生代采用复制算法,YGC后大多数的对象将被回收,活着的进入s0,再次YGC,活着的Eden+s0区的对象将被复制到s1中,再次YGC,则 Eden+s1 ->s0… 当年龄足够时(可以设置年龄),对象将进入老年代,如果年龄不够但s区装不下时,也会将对象放入老年代。
- 老年代满了将会进行FGC,FGC不应该频繁进行
- 除Epsilon 、ZGC 、Shenandoah之外的GC都是使用逻辑分代模型,G1是逻辑分代,物理不分代,除此之外不仅逻辑分代,而且物理分代。
- 分配担保:YGC期间,survivor区的空间不够了,空间担保直接进入老年代
常见的垃圾回收器
Serial系列
随JDK诞生的垃圾回收器是Serial,Serial是一种单线程串行的年轻代垃圾回收器,垃圾回收时会Stop The World(所有工作线程停止),暂停所有工作线程,进行垃圾回收。而Serial Old是老年代的Serial。
Parallel系列
为了提高效率,减少STW时间,出现了Parallel Scavenge,简称PS,该垃圾回收器在垃圾回收时启动多个线程并行的进行垃圾回收,减少了STW时间,Parallel Old,简称PO则是老年代的Parallel Scavenge。
CMS
随着内存空间的进一步扩大,PS\PO的STW时间越来越长,因此出现了CMS这种并发垃圾回收器,而ParNew是为了配合CMS而诞生的年亲代垃圾回收器,CMS标记时使用三色标记法,三色标记将对象分为三种颜色,不同颜色代表对象的不同状态(如下图1),使用该算法会出现漏标的问题(当黑色对象指向一个白色对象,而原来指向白色对象的灰色对象丢弃了 指向该白色对象的指针时,会使得该白色对象漏标,解决办法有两种,如图3,CMS使用第一种解决办法)。
-
CMS (ConcurrentMarkSweep)老年代垃圾回收器 使用标记清除算法的并发的垃圾回收器, 垃圾回收和应用程序同时运行,降低STW的时间, CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定。 CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收,所以应尽量避免fullGC。
-
CMS的几个过程 初始化标记(STW)、并发标记、重新标记(STW)、清理
图1:
图2:
图3
G1
G1(Garbage First),该垃圾回收器是在CMS的基础上进一步的改进,它是用到了三色标记+SATB这第二种解决漏标问题的方案,它的特点包含,第一,和CMS一样并发收集,第二,它可以预测GC的暂停时间,第三,该垃圾回收器相比于CMS吞吐量层面有所下降,但相应时间上占据有利地位。
G1采用了逻辑分代,物理不分代的全新策略,它将内存划分为一个个的region,将内存回收放到一个个的小region上,而不是整个年轻代或老年代。
G1的有3种类型的回收,不同的条件会触发不同的回收,YGC和MixedGC也可能混着来。第一,年轻代的回收YGC,普通的GC过程;第二,MixedGC(类似CMS的过程)当对象分配占据整个堆大小的某个值时(默认45%,可设定),可以触发MixedGC(年轻代,老年代都会回收);第三,fullGC,当对象不能找到一个region分配时,出发fullGC,该过程是单线程的GC过程,需要避免。
回收采用复制算法;回收不是所有的region都回收,而是选择垃圾最多的region进行垃圾回收,这也是Garbage first的由来
G1的年轻代与老年代没有固定的大小比例(年轻代占据整个堆的5%-60%),处于动态的变化中,这也是G1预测暂停时间的保证。
G1中的两个概念解读
- Cset(collocation set)G1在进行垃圾回收时,不是将整个内存中的年轻代或老年代都回收,而是选择垃圾最多的一些region进行,而Cset即为这些region的集合。
- Rset(Remember set)G1的每个region里会有一部分内存用来记录当前region中的对象是否有其他region中的对象的引用指向(是否有其他region中的对象指向当前region中的对象)。
ZGC
是对G1的进一步改进,算法:ColoredPointers + LoadBarrier
Shenandoah
算法:ColoredPointers + WriteBarrier
Eplison
Eplison只有在debug的时候会用到。
垃圾回收器总结
-
垃圾收集器跟内存大小的关系,可以说垃圾回收器的发展正是由于内存的扩大而推动。
Serial 几十兆
PS 上百兆 - 几个G
CMS - 20G
G1 - 上百G
ZGC - 4T - 16T(JDK13) -
垃圾回收器的发展过程:
随JDK诞生的Serial->为提升效率,出现PS\PO的多线程并行回收器-> 内存进一步加大,为了减少STW时间,出现CMS\PN并发的垃圾回收器-> 内存进一步扩大出现G1、ZGC -
1.8默认的垃圾回收:PS + ParallelOld
-
所有的垃圾回收器都不可避免的有STW。
转载:https://blog.csdn.net/IT_0008/article/details/106268656