• Java虚拟机笔记(三):垃圾收集算法


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

    标记清除算法是最基础的收集算法,其他收集算法都是基于这种思想。

    标记清除算法分为“标记”和“清除”两个阶段:首先标记出需要回收的对象,标记完成之后统一清除对象。

    具体过程如下图所示:

    优点:简单直观容易实现和理解

    缺点:①效率问题:标记和清除两个过程的效率都不高。

              ②空间问题:标记清除之后会产生大量的不连续的内存碎片。

    空间碎片太多可能会导致以后再在程序运行过程中需要分配较大对象时,无法找到足够的连续的内存碎片而不得不提前出发另一次的垃圾收集动作。

    二、复制(Copying)算法

    为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。

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

    具体过程如下图所示:

     

    优点:每次都是对其中的一块进行内存回收,不会产生碎片等情况,只要移动堆顶的指针,按顺序分配内存即可,实现简单,运行高效。

    缺点:内存缩小为原来的一半。

    很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。

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

    为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。 

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

    具体过程如下图所示:

    优点:不会产生内存碎片,且内存使用充分。

    缺点:在标记-清除的基础上还需进行对象的移动,成本相对较高。

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

    分代收集算法是目前大部分JVM的垃圾收集器采用的算法。这种算法没什么特别的,无非是上面内容的结合罢了,它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域,然后对不同的区域选择适合的收集算法。

    一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation)。

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

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

    新生代:适合采用复制算法进行垃圾收集,对象分布在Eden/S0/S1三个区。
    老年代:适合采用标记-整理算法进行垃圾收集。

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

    五、同类随笔

    Java虚拟机笔记(一):类加载机制

    Java虚拟机笔记(二):GC垃圾回收

    Java虚拟机笔记(三):垃圾收集算法

    Java虚拟机笔记(四):垃圾收集器

    Java虚拟机笔记(五):JVM中对象的分代

    参考:《深入理解Java虚拟机》

  • 相关阅读:
    《Apache服务用户身份验证管理》RHEL6.3
    什么是WEB服务器?
    《squid网卡代理的实现》RHEL6.3——条理清晰,步骤明确
    转:【Java并发编程】之十一:线程间通信中notify通知的遗漏(含代码)
    转:【Java并发编程】之十:使用wait/notify/notifyAll实现线程间通信的几点重要说明
    转:【Java并发编程】之九:死锁(含代码)
    转:【Java并发编程】之八:多线程环境中安全使用集合API(含代码)
    转:【Java并发编程】之七:使用synchronized获取互斥锁的几点说明
    【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)
    转: 【Java并发编程】之五:volatile变量修饰符—意料之外的问题(含代码)
  • 原文地址:https://www.cnblogs.com/study-everyday/p/7055646.html
Copyright © 2020-2023  润新知