JVM——垃圾回收机制

重点

内存分配策略、垃圾收集器(G1)、GC算法、GC参数、对象存活的判定
垃圾回收需要思考的3件事情:

  • 哪些内存需要回收
  • 什么时候回收
  • 如何回收

对象存活的判定

垃圾回收之前,要判断哪些对象需要回收。

引用计数算法

给对象添加一个引用计数器,当有引用引用这个对象时,计数器加一,当引用失效时,计数器减一,当计数器为0时,这个对象就是没有被引用的。
这种方式实现简单,判断效率也高,但是这种方式没有办法解决互相循环引用的问题,所以Java虚拟机没有使用这个方式。互相循环引用,比如有两个对象objA和objB,都有一个实例instance,并且objA.instance=objB,objB.instance = objA。实际上这两个对象都不可能再访问,但是因为互相引用,导致计数器的值不为0,所以GC收集器没有办法回收。

可达性分析算法

Java采用可达性分析算法来判定这个对象是否存活。可达性分析算法的思想:
以“GC root”对象作为起点,从这个节点向下搜索,搜索走过的路径称为引用链,当一个对象到这个“GC root”没有任何引用链可以到达时,证明这个对象是不可用的,判定这个对象为可回收对象。
在Java语言中,可以作为GC root 的节点的包括以下几种:

  • 虚拟机栈中的引用对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中native方法引用的对象

引用

在JDK1.2之前对引用的描述:如果reference类型数据存储的是另一块内存地址的起始地址,就称这块内存代表着一个引用。
但是这种描述过于狭隘,希望描述一个对象:当内存空间还足够时,这个对象能保留在内存中。如果内存空间在进行垃圾回收之后还是很紧张,则可以抛弃这类对象。
在JDK1.2之后,Java引用的概念得到了扩充,将引用分为:强引用、软引用、弱引用和虚引用。4中引用的强度依次减弱。
强引用:强引用在代码中普遍存在的,类似“Object obj = new Object()”这种,只要强引用还在,垃圾回收就不会回收被引用的对象。
软引用:用来描述还有用当非必须的对象。对于软引用关联的对象,在发生内存溢出之前,会把这些对象列入回收范围进行第二次回收。如果这次回收内存空间依然不足,才会发生内存溢出。使用SoftReference类实现软引用。
弱引用:也是用来描述非必须对象,但是强度弱于软引用。当垃圾收集器工作时,无论内存空间是否充足,都会将回收掉被弱引用关联的对象。使用WeakReference类实现弱引用。
虚引用:也称为幽灵引用或者幻影引用。一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法虚引用取的一个对象的实例,其唯一的目的就是在这个对象被垃圾回收时收到一个通知。使用P’hantomReference类实现。

生存还是死亡

在可达性分析算法中,不可达的对象,也并非一定会被回收,一个对象真正被回收,至少要经历2次呗标记的过程,如果可达性分析之后,这个对象没有个GC root关联上,那么他将会被第一次标记,并且进行一次筛选,筛选条件是这个对象是否有必要执行finalize方法。如果没有覆盖finalize方法或者finalize方法已经被虚拟机执行调用过了,则被视为没必要执行。如果是有必要执行finalize方法,那么这个对象会被放置在一个F-Queue队列中,然后由一个低优先级的Finalizer线程去执行。如果对象在此期间能够重新和引用链上的任何对象关联上,那么这个对象就不会呗回收。否则,进行第二次标记时,这个对象就会被真正的回收了。

回收方法区

在方法区中进行垃圾回收的性价比比较低,因为在堆中,进行一次垃圾回收,可以回收70%~95%的空间。方法区垃圾回收主要分为两部分,废弃的常量和无用的类。回收废弃的常量和回收对象类似,没有引用和这个常量进行关联,那么可能会被回收。回收废弃的类需要满足下列条件:

  • 该类的所有实例都已经被回收。
  • 加载该类的ClassLoader已经被回收。
  • 该类对应的Class对象,没有在人任何地方被引用,无法在任何地方可以通过反射访问该类的方法。
    满足上述条件,可以对类回收,但是不是一定要回收。可以通过参数设置。

几种垃圾收集算法的思想

标记——清除算法

标记清除算法是最基础的垃圾收集算法,分为“标记”和“清除”两个阶段,首先标记出所有的要被回收的对象,标记完成之后统一回收被标记的对象。这种算法主要不足有两个:标记和清除两个过程的效率都不高。另外一个是空间问题,标记清除之后会造成大量不连续的空间碎片,在程序需要分配比较大的对象的空间时,没有足够的内存不得不触发下一次垃圾回收。

复制算法

复制算法将内存分为大小相等的两块,每次只使用一块,当一块用完了,将存活的对象复制到另一块,然后将使用那一块全部清除掉。这种算法不用考虑内存碎片的情况,实现简单,高效,但是代价是将内存缩小了一半。
在实际的商业虚拟中,使用复制算法来回收新生代,将内存分为一个较大的Eden空间和2块比较小的survivor空间,每次使用一块Eden和一块survivor空间,每次回收时,将Eden和survivor中存活的对象一次性复制到另一块survivor中,然后清零掉刚刚使用的Eden和survivor空间。在Hotspot中,Eden和survivor比例是8:1,所以有10%的空间会被“浪费”,当survivor不够时,需要依赖其他内存进行担保。

标记整理算法

老年代的特点是对象存活率比较高,所以出现了标记整理算法,标记需要被回收的对象,然后对存活的对象向另一端移动,然后直接将边界外的对象清除。

分代收集算法

当前商业虚拟机采用的这种算法,一般将Java堆分成2代,新生代和老年代,新生代每次垃圾收集都会有大量的对象被回收,少量存活,所以使用复制算法,而老年代对象存活率比较高,使用标记清除或者标记整理算法。

垃圾收集器

垃圾收集器是JVM内存回收的具体实现。在JDK1.7 update 14之后的Hotspot提供了正式商用的G1收集器,在这个虚拟机中,包含的垃圾收集器如图所示:
在这里插入图片描述
上图描述的垃圾收集器,如果两个之间有连线,那么说明这两个垃圾收集器可以搭配使用,垃圾收集器所处的区域表示是新生代的垃圾收集器还是老年代的垃圾收集器。

Serial收集器

Serial垃圾收集器是最基本的垃圾收集器,也是发展最悠久的垃圾收集器。在JDK1.3之前,是新生代里唯一的垃圾收集器。Serial收集器是单线程的垃圾收集器,在进行垃圾回收时,必须暂停其他线程的工作。Serial或者Serial Old收集器运行的示意图如图:
在这里插入图片描述
Serial收集器的有点就是:简单而高效,因为没有线程的交互开销。Serial收集器也是虚拟机client模式下默认的新生代垃圾收集器。

ParNew收集器

ParNew收集器是Serial收集器的多线程版本。运行图如图:
在这里插入图片描述
ParNew收集器是运行在Server模式下首选的垃圾收集器,除性能外,另外一个原因就是ParNew收集器是新生代除了Serial唯一个能和CMS收集器配合使用的。

Parallel Scavenges收集器

Parallel Scavenges收集器和ParNew收集器一样,是新生代的垃圾收集器,使用复制算法,也是并行的多线程的收集器,和ParNew收集器不同之处在于,Parallel Scavenges收集器目标在于达到可以控制的吞吐量。吞吐量=运行用代码时间/(运行用户代码时间+垃圾收集时间)。提高吞吐量,可以高效的利用CPU,适合在后台运算而不需要太多交互的任务。除了目标不一样,Parallel Scavenges收集器的自适应调节策略也是和ParNew收集器重要的区别。

Serial Old收集器

Serial Old收集器是Serial收集器的老年版本,单线程,标记-整理算法。主要用户Client模式下使用。如果再Server模式 下,Serial Old收集器还有两个用途:
在JDK1.5之前和Parallel Scavenges收集器搭配使用
作为CMS收集器的后备预案。

Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,搭配使用,可以在注重吞吐量以及CPU资源敏感的情况下发挥出良好的效果。
在这里插入图片描述

CMS收集器

CMS收集器全称Current Mark Sweep,是一种以获取最短回收停顿为目标的收集器。CMS收集器在互联网或者B/S系统服务器上都有良好的体验。CMS收集器基于标记-清除算法。整个过程分为4部:
初始标记、并发标记、重新标记、并发清除。
其中,初始标记和重新标记仍然需要短暂的停顿。
初始标记用来直接标记和GC Root关联的对象,耗时很短。并发标记则是进行GC Root Tracing的过程。重新标记用来在进行并发标记时用户程序继续运行而发生改变那一部分的对象的标记。
在这里插入图片描述
CMS收集器主要优势就是并发收集,低停顿。但是CMS也有缺点:

  • 对CPU资源敏感。当CPU数量少的时候,在收集垃圾时,会造成用户执行的程序速度忽然降低。
  • 无处理浮动垃圾
  • 标记-清除造成垃圾碎片。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值