JVM垃圾回收机制

GC概述

JVM的垃圾回收机制这里我们称为GC。众所周知,Java语言不需要像c++那样需要自己申请内存,自己释放内存,这些都是JVM帮我们做好了的。学习GC的原理有助于更好的帮助我们编写程序。

JVM定义了许多不同的运行时数据区,其中程序计数器、虚拟机栈、本地方法栈三个区域会随着线程而生,随线程而灭,故这几个区域不需要考虑回收问题。垃圾回收主要关注Java堆和方法区。

要执行垃圾回收,关键在于两点,一是判断对象是否存活,二是释放垃圾对象所占用的空间。

判断对象是否存活

垃圾回收器在执行之前,会先判断堆中哪些对象还存活,哪些已经死去。主要使用两种算法。

引用计数算法

给对象添加一个引用计数器,每当一个地方引用它的时候,计数器的值就+1,当引用失效时,计数器就减1。当对象的引用值为0时,就不会被再使用。此算法很少被使用,因为无法解决互相循环引用的问题。

若对象A中含有对象B的引用,对象B中含有对象A的引用。此时,对象A和B的引用计数器都不为0.但是在系统中却不存在任何第3个对象引用了A或B。即A和B是应该被回收的垃圾对象,但由于垃圾对象间相互引用,从而垃圾回收器无法识别,引起内存泄漏。

可达性分析算法

通过一系列称为GC roots的对象作为起点,从这些起点开始向下搜索,搜索所走过的路径称为引用链。当一个对象没有任何引用链时,则证明此对象是不可用的,可以被回收。

所以JVM判断对象需要存活的原则是:能够被一个根对象到达的对象。对象A引用了对象B,那么A到B可达。

GC Root对象集合:

a:Java虚拟机栈(栈帧中的本地变量表)中的引用对象。(当前栈帧的对象引用)
b:方法区中的类静态属性引用的对象。(static对象引用)
c:方法区中的常量引用的对象。(final对象引用)
d:本地方法栈中JNI本地方法的引用对象。

除了堆之外,方法区中的“废弃常量”和“无用的类”需要回收以保证永久代不会发生内存溢出,检测方法区垃圾对象的方法:
1、判断废弃常量的方法(不再需要的常量):如果常量池中的某个常量没有被任何引用所引用,则该常量是废弃常量
2、判断无用的类(不再需要的class文件):

1)该类的所有实例都已经被回收,即Java堆中不存在该类的实例对象
2)加载该类的类加载器已经被回收
3)该类所对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射机制访问该类的方法。

当持久代(方法区)满时,将触发Full GC,根据以上标准清除废弃的常量和无用的类。

垃圾收集算法

标记-清除(Mark-Sweep)

标记-清除算法分为两个阶段:标记阶段和清除阶段。

标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。

标记-清除算法容易实现,但容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。
这里写图片描述

复制(Copying)

将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。

复制(Copying)算法实现简单,运行高效且不容易产生内存碎片,但是能够使用的内存缩减到原来的一半。

Copying算法的效率跟存活对象数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将大大降低。
这里写图片描述

标记-整理(Mark-Compact)

该算法标记阶段和标记-清除(Mark-Sweep)一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
这里写图片描述

分代(Generation Collection)

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。
核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下,将堆区划分为老年代(Tenured Generation)和新生代(Young Generation)。将方法区划分为永生代。如下图所示:
这里写图片描述

老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数比较少,但是实际中并不是按照1:1的比例来划分新生代的空间,一般来说是将新生代划分为一块较大的Eden区和两块较小的Survivor区,每次使用Eden区和其中一块Survivor区,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor区中,然后清理掉Eden和刚才使用过的Survivor区。而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。

垃圾收集器

如下图所示,垃圾回收算法一共有7个,3个属于年轻代、三个属于年老代,G1属于横跨年轻代和年老代的算法。JVM会从年轻代和年老代各选出一个算法进行组合,连线表示哪些算法可以组合使用。
这里写图片描述

Serial/Serial Old(串行收集器)

Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。

Serial收集器是针对新生代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是Mark-Compact算法,可以和所有的新生代收集器组合使用。

优点是实现简单高效,缺点是会给用户带来停顿。

这里写图片描述

ParNew(新生代)

ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。可以和Serial Old、CMS组合使用,采用复制算法,使用多线程进行垃圾回收,回收时会导致Stop The World,其它策略和Serial一样。
这里写图片描述

Parallel Scavenge(新生代)

Parallel Scavenge收集器是一个新生代的多线程收集器(并行收集器),回收时会导致Stop The World,其采用的是Copying复制算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。

Parallel Old(老年代)

Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact(标记-整理)算法,会对垃圾回收导致的内存碎片进行整理。只能和Parallel Scavenge组合使用,关注吞吐量的系统可以将Parallel Scavenge+Parallel Old组合使用。

这里写图片描述

CMS(并发收集器)

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep(标记-清除)算法。适用于对响应时间要求比较高的系统。

并行收集器:多条垃圾收集线程并行工作,而用户线程仍处于等待状态
并发收集器:垃圾收集线程与用户线程一段时间内同时工作(不是并行,而是交替执行)

这里写图片描述

CMS垃圾回收阶段

CMS是并发算法,表示垃圾回收和用户进行同时进行,但是不是所有阶段都同时进行,在初始标记、重新标记阶段还是需要Stop the World。CMS垃圾回收分这四个阶段

1、初始标记(CMS Initial mark)Stop the World,仅仅标记一下GC Roots能直接关联到的对象,速度快
2、并发标记(CMS concurrent mark)进行GC Roots Tracing,时间长,不发生用户进程停顿
3、重新标记(CMS remark)Stop the World,修正并发标记期间因用户程序继续运行导致标记变动的那一部分对象的标记记录,停顿时间较长,但远比并发标记时间短
4、并发清除(CMS concurrent sweep) 清除的同时用户进程会导致新的垃圾,时间长,不发生用户进程停顿

缺点

1、对CPU资源非常敏感
2、CMS收集器无法处理浮动垃圾,即清除时用户进程同时产生的垃圾,只能等到下次GC时回收
3、因为是使用“标记-清除”算法,所以会产生大量碎片

G1(垃圾优先收集器)

G1收集器是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。G1收集器是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。

在G1中,堆被划分成许多个连续的区域(region)。每个区域大小相等,在1M~32M之间。JVM最多支持2000个区域,可推算G1能支持的最大内存为2000*32M=62.5G。区域(region)的大小在JVM初始化的时候决定,也可以用-XX:G1HeapReginSize设置。在G1中没有物理上的Yong(Eden/Survivor)/Old Generation,它们是逻辑的,使用一些非连续的区域(Region)组成的,个数不是固定的,使用起来很灵活。

这里写图片描述

G1并不是一款实时垃圾收集器(real-time collector)。G1执行垃圾回收的处理方式与CMS相似,能以极高的概率在设定的目标暂停时间内完成,但不保证绝对在这个时间内完成。

G1在全局标记阶段(global marking phase)并发执行, 以确定堆内存中哪些对象是存活的。标记阶段完成后,G1就可以知道哪些heap区的empty空间最大。它会首先回收这些区,通常会得到大量的自由空间。这也是为什么这种垃圾收集方法叫做Garbage-First(垃圾优先)的原因。

G1适用的场景

1、Full GC 次数太频繁或者消耗时间太长;
2、对象分配的频率或代数提升(promotion)显著变化;
3、受够了太长的垃圾回收或内存整理时间(超过0.5~1秒)

参考

http://www.importnew.com/15311.html
http://blog.csdn.net/renfufei/article/details/41897113
http://www.cnblogs.com/lushilin/p/6140507.html
http://blog.csdn.net/ymrfzr/article/details/51354380
http://blog.csdn.net/qq_33048603/article/details/52727991

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值