关于 Java垃圾回收机制(2)

  在确定了哪些垃圾可以被回收后,垃圾收集器要做的事情就是开始进行垃圾回收,但是这里面涉及到一个问题是:如何高效地进行垃圾回收。由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确的规定,因此各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器,就以最常用的HotShot为例,所以在此只讨论几种常见的垃圾收集算法的核心思想。

1、Mark-Sweep(标记-清除)算法

  这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。图解来自网络,很好的说明了标记-清楚算法的处理前和处理后的内存分布。

下面所有的图是模拟内存块,红色为未使用内存块,灰色为待回收对象内存块,黄色为存活对象

回收之前

关于 Java垃圾回收机制

回收之后

关于 Java垃圾回收机制

  很容易看出这样的操作是有弊端的,这样讲标记的对象的清楚后,内存块就变的零零散散,如果现在有一个对象占用的内存很大,这个时候必须要在执行一遍垃圾回收,为这个大的对象腾出空间。

2、Copying(复制)算法

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

回收之前

关于 Java垃圾回收机制

回收之后

关于 Java垃圾回收机制

  复制算法会提前空出一般的内存,在垃圾回收的时候将存活的对象移动的另外一半内存,这样内存的移动消耗太大,虽然内存不是零散的,但是代价太高。

3、Mark-Compact(标记-整理)算法

  为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:

回收之前

关于 Java垃圾回收机制

回收之后

关于 Java垃圾回收机制

4、Generational Collection(分代收集)算法

  分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,并不是回收所有,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。可以调用System.gc()方法查看回收情况。

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

  而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。

  注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。

三、典型的垃圾收集器

  下面都是些概率性的东西,笔者看得也似懂非懂,直接搬过来分享给大家

1.Serial/Serial Old

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/231afab9e54d47a134f097cea8cc61e8.html