深入理解JAVA虚拟机(四)JVM垃圾收集算法

​ 从前面章节我们了解到,JVM内存划分最大的一块是堆内存,堆内存是用来存放new出来的对象的,JVM通过可达性分析算法,如果对象两次标记不可达,则判定为“死亡”,这时候JVM垃圾回收器就会回收该对象,这个流程就是垃圾回收算法的标记 - 清除,但是这还没完,下面详解垃圾收集算法:

  1. 标记 - 清除算法

    这是最基础最简单的算法,先是标记需要回收的对象,然后统一回收被标记的对象,如下图:

    jvm标记清除

    但是这个算法有两个不足:1)效率不高 2)回收后会产生大量不连续的内存碎片空间,如果后续程序需要分配较大对象的内存时,可能无法匹配到合适的连续内存。

  2. 复制算法

    为解决标记-清除算法的不足,“复制算法”出现了,它将内存分为大小相等的两块,一块是活动内存,一块是空闲内存,清理步骤如下:

    2.1) 找出活动内存中存活的对象

    2.2) 复制存活对象到空闲内存

    2.3) 清理活动内存

    2.4) 活动内存变成空闲内存,之前的空闲内存变为活动内存

    复制算法优点:每次只需回收一半内存空间,效率提高;回收后存活对象放一边,空闲内存放另一边,解决了内存碎片的问题,下次分配内存,只需要指针往空闲内存区域移动即可,即前面第二章讲的“指针碰撞”分配法。

    复制算法缺点:内存缩小一半,严重浪费。

  3. 复制算法 - 内存划分优化(新生代)

    复制算法内存对半划分确实很浪费,因为据研究表明,在新生代中的对象98%都是“朝生夕死”的,所以不需要将内存对半划分,而是采用8:1:1的比例,划分为Eden、Survivor1、Survivor2三个区域,清理步骤如下(原理和第2点一样):

    3.1) 在Eden和Survivor1中找出活动内存中存活的对象

    3.2) 复制存活对象到Survivor2

    3.3) 清理Eden和Survivor1

    3.4) Eden和Survivor2变为活动内存区,之前的Survivor1变成空闲内存

    这种分配方式很好的解决了内存浪费的问题(当Survivor2空间不够用的时候,会从老生代分配)

    由于对象的存活周期不同,JVM采用“分代收集”将内存划分为几块,一般是划分为新生代和老生代,前面也说了,在新生代中的对象98%都是“朝生夕死”的,所以这种复制算法的方式,适合新生代,而不适用老生代,老生代采用标记-整理算法:

  4. 标记-整理算法

    针对老生代,对象存活时间较长,不适合移来移去,10%的空闲内存也不够放的,所以出现了“标记-整理”算法,和“标记-清除”算法不同的是,标记过程执行完后,不是对可回收对象进行清理,而是将所有存活的对象都移动到一块,然后清理掉另外一边的内存,这样也能解决内存碎片和效率的问题,比较适合老生代,如下图:

    jvm标记整理

坚持原创技术分享,您的支持将鼓励我继续创作!
分享