JVM垃圾回收与内存分配(二)

来源:互联网 发布:顶尖恢复软件 编辑:程序博客网 时间:2024/06/05 23:56

垃圾收集算法

垃圾收集算法很多种,最基础的是标记-清除算法,其他都是根据这个算法对其不足进行改进。具体实现比较复杂,故只介绍基本思路。

标记-清除算法

垃圾收集中最基础的算法,它分为标记和清除两个阶段,标记阶段在上一篇文章《 JVM垃圾回收与内存分配(一) 》中介绍的标记过程一样,首先标记出所有需要回收的对象,然后统一回收。它主要有两个缺点:

1、效率问题,标记和清除两个过程的效率都不高。

2、空间问题,标记和清除过后,容易产生大量的不连续的内存空间,即空间碎片。若程序需要申请较大的对象时容易由于没有连续的空间而不得不提前促发下一次的垃圾回收。

复制算法

复制算法是为了解决“标记-清除算法”中的效率问题。其基本思路是:将可用内存按照容量分为大小相等的两块,如A和B。需要分配内存时,只使用A,如果A的空间足够,不进行回收,当A的剩余空间不足时,将A中所有活着的对象全部copy到B中,之后再使用B,一次性全部清除A内存,以此类推。

这种方法效率较高,实现简单。但问题也很明显,它使我们的可用内存缩减了一半,代价比较大。

大部分的商用虚拟机都是采用复制算法来负责堆中新生代的回收的。

jvm堆 = 新生代 + 老年代 + 持久代。

有的虚拟机也将老年代和持久代合并为一。即jvm堆 = 新生代 + 老年代 。

新生代,用来存放新生成的对象的。

老年代,用来保存一些大的对象和经过几次垃圾回收之后仍然存活的对象。

新生代和老年代的比例,可以通过jvm参数-XX:newRatio设置。

之前说过,原始的复制算法代价比较大,缩减了一半内存,在商用虚拟机划分新生代时,采用如下分配策略:

将新生代分为一个Eden区域和两个Survivor区域(不再采用原始的1:1分配),他们之间的比例默认是8:1:1(可以通过-XX:survivorRatio设置)。

新生代-复制算法

每次创建新对象时,都只是用新生代中的一个eden和一个survivor,当 内存不足时,将这个eden和这个survivor中活着的对象转移到另一个survivor中(此时我们假设另一个survivor的空间够用,不够用的情况稍后解释),然后清除掉之前的Eden和Survivor。

当另一个survivor中的空间不足以保存垃圾回收时保存下来的活着对象时,就需要将大的对象放入老年代中。

采用这种垃圾回收算法,新生代的内存空间有90%可用(80%+10%)比之前的50%提高了很多。

标记-整理算法

若对象的存活率较高时,采用复制算法就会大量的复制对象,效率比较慢。

老年代的对象存活率较高,故采用“标记-整理算法”,前半部分的标记与“标记-清除算法”一样,但标记之后不是马上清除所有的无用对象,而是将或者的对象全部向一侧整理,使内存的某一侧全部为活着的对象,然后一次性清除边界之后的所有内存。

这样就有了足够大的连续内存空间。

分代收集算法

目前的商业虚拟机都是采用此种算法,它只不过是将虚拟机的堆内存根据对象的生命周期不同分为几个部分,简单来说,将堆分为新生代(对象生命周期较短)、老年代(生命周期较长),针对新生代,采用复制算法,通过较少的复制达到清理的目的。针对老年代,存活率较大,不能采用复制,就得需要标记-清理或标记-整理算法来实现,采用标记-整理算法的比较多。



0 0
原创粉丝点击