天天看点

JVM垃圾回收机制以及算法垃圾回收机制垃圾收集算法

JVM垃圾回收机制以及算法

  • 垃圾回收机制
    • 那些内存需要回收
  • 垃圾收集算法
    • 标记-清除法
    • 复制算法
    • 标记整理法
    • 分带收集算法

垃圾回收机制

垃圾收集(Garbage Collection, GC),作为java的伴生产物,与c++相比,墙外的人想进来,墙内的人想出去。

那些内存需要回收

针对于java内存运行时区域的各个部分,其中程序计数器、虚拟机栈(简称 栈)、本地方法栈3个区域随线程而生,随线程而灭,栈中的栈帧随着方法的进入和推出而有条不紊的进行着出栈和入栈操作。每一个栈帧分配多少内存基本上在类结构确定的时候就已经是可知了,因此在这几个区域的内存分配和回收不需要进行过多的考虑,因为方法结束的时候,内存就随着回收了。但是java堆和方法区则不一样,我们只有在运行期间才能知道要创建那些对象,和一个方法中的多个分支需要分配的内存也可能是不一样的,这些内存的分配和回收都是动态的,因此需要垃圾收集齐过多的关注。针对与那些内存需要回收,首先需要判断该对象是否还活着,其中判断包括一下两个算法:

1. 引用记数法

引用记数法判断原理:给对象添加一个计数器,每当一个地方引用该对象,则计数器+1;当引用失效的时候,计数器-1;任何计数器为0的对象就是不可能在被使用。

客观的说,该算法的实现简单,判断效率也不错,但是有主流的java 虚拟机并没有使用该算法,最主要的原因就是,它很难解决对象循环依赖的问题。举个简单的例子 a->b->a,除此之外,这两个对象在无任何引用,但是两个对象的程序计数器并不为0,但是实际上这两个对象都是需要被回收的。

2. 可达性分析算法

在主流的虚拟机中,都是通过该算法来判断对象是否存活。该算法原理,就是通过一系列的“GC Roots”的对象作为起点,从这些节点向下寻找,寻找所经过的路径成为引用链,凡是没有在引用链路上的对象,都是需要被回收的。

在jvm中,可以作为GC Roots的对象包括以下几种:
           
  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中的静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈引用的对象

    针对与上述描述中,多次提到的引用,在java中将引用分为:强引用、软引用、弱引用、虚引用等4种,这4种引用强度如下:

  • 强引用:类似于new Object(),只要强引用还存在,对象就一定不会被回收
  • 软引用:用来描述一些还有用但并非必须的对象,对于这类对象,在系统将要内存溢出异常之前,回将这些对象进行二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。其中在jdk中,提供SoftReference类来实现软引用。
  • 弱引用:用来描述非必需对象的,强度比软引用还弱,被弱引用的对象只能活到下次垃圾回收之前。也就是只要垃圾收集器工作,该对象就一定会被回收。实现类为WeakReference。
  • 虚引用:最弱的引用关系,无法通过虚引用获取对象实例。作用是为了就是能在这个对象被收集器回收时收到一个系统通知。实现类为:PhatomReference。

垃圾收集算法

垃圾收集算法包括有一下几种:
           

标记-清除法

该算法是最基础的,该算法包括两个步骤:标记和清除。原理:标记出所有需要回收的对象,在标记完成后统一进行回收。

缺点:

1.效率不高,标记和清除的效率都不高。

2.标记清除后会产生大量不连续的碎片,在程序需要分配大对象的时候,由于找不到连续内存而需要再次触发回收动作。

复制算法

为了解决标记-清除算法的效率问题,该算法被开发出来。原理:将内存按容量分为大小相等的两块,每次只是用其中的一块。当这一块的内存使用完毕时,将还存活的对象复制到另外一块内存上,然后在把已经使用内存空间一次性清理掉。

缺点:内存使用率降低为越来的50%。

这种算法基本上应用在新生代上,因为新生代的对象基本上都是朝生夕死,并不需要按照1:1的比例分配内存,而是将内存分为一快较大的Eden(新生)空间和两块较小的Survivor(存活)空间,每次只使用Eden和一块Servivor空间。当回收时,将Eden和Survivor中还存活的对象复制到另外一块Survivor空间中,最后清除掉Eden和刚才使用的Survivor空间。所以其实新生代的最大内存是不足100%的,但是只有少部分内存会被浪费。当Survivor不够用的时候,就需要依赖别的内存(老年代)进行分配担保。

标记整理法

复制算法在对象存活较多的时候,效率就会显得力不从心。更关键的是,如果不想浪费50%的空间,就需要额外的空间进行担保,以应对对象100%存活的极端情况,所以老年代一般不会选择该类算法。

针对老年代的特点(存活对象较多),标记整理算法应用而生。标记过程与标记-清除法的标记过程一样。但后续步骤不是直接对可回收对象进行回收,而是将存活对象向一方移动,然后直接清除掉边界以外的内存。

分带收集算法

该算法就是针对与jvm不同的内存块使用不同的算法机制。新生代使用复制算法,老年代使用标记整理法。

继续阅读