JVM 基础:垃圾回收

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Gdeer/article/details/88783811

一、标记垃圾

1.1 引用计数法

每个对象记录一个被引用值,被引用一次加一次,引用取消减一次。进行垃圾回收时,如果被引用值是0,就说明是垃圾。

问题:循环引用
当 A 引用 B,B 引用 A 时,他们都不会被回收。

1.2 可达性分析

从一系列根节点进行遍历,找到能到达的每个点。不在这些点内的对象就是垃圾。

解决了循环引用问题。

可达性分析的根节点:

  • 虚拟机栈中引用的对象
  • 方法区中的静态对象
  • 方法区中的常量对象
  • 本地方法栈中的对象

二、回收垃圾

2.1 标记清除

过程:

  • 标记:即第一节所述的标记方法
  • 清除:每个对象依次清除。

优点:简单
缺点:

  • 效率:两个过程的效率都不高
  • 碎片:清除之后会产生很多不连续的碎片

回收前:
在这里插入图片描述

回收后:

在这里插入图片描述

空白的地方就是清除后的可用空间,散布得很碎。

2.2 复制(标记-复制-清除)

过程:

  • 标记:即第一节所述的标记方法
  • 复制:将内存平分成两部分,每次只使用其中的一块,当一块用完了,把它里面还要保留的对象复制到另一块上,然后清除这一整块。下次那块满了就重复同样的过程。
  • 清除:清除掉一整块空间

优点:解决了效率问题、碎片问题

缺点:空间的利用率不高

复制过程图解:

复制前:

在这里插入图片描述

复制后:

在这里插入图片描述

复制、清除完成后,原有的 to 区就变成 from 区,开始接收对象。

复制算法的优化:

当每次要保留的对象很少时,由于复制算法将空间平分为两半,会有很大一部分空间浪费掉。

在这里插入图片描述

这时将内存划分为三块后可以解决这个问题,使用时,A 区 + 公共区可以当做原有的 from 区,垃圾回收时,把要保留的对象放在B 区,然后B 区 + 公共区就可以当做下一轮的from 区,开始接收对象。

在这里插入图片描述

2.3 标记整理(标记-整理-清除)

过程:

  • 标记:即第一节所述的标记方法
  • 整理:将要保留的对象全部移动到靠前的一端,这样就把要清除的对象都放在连续的一段空间里
  • 清除:清除掉连续的一段空间

优点:解决了碎片问题
缺点:效率不高

整理过程图解:

整理前:

在这里插入图片描述

整理后:

在这里插入图片描述

2.4 分代收集

根据对象存活周期的不同,把 java 堆分为新生代、老年代。

新生代中,对象的存活率不高,复制算法的成本低,且有老年代可以进行分配担保,所以使用复制算法。即包括一个 Eden 区(即上述的公共区)、一个 from Survivor 区(A 区)、一个 to Survivor 区(B 区)。

老年代中,对象的存活率较高,且无人担保,所以使用标记清除或标记整理法。

在这里插入图片描述

分代模型:

    • 新生代(minor GC)
      • Eden
      • from Survivor
      • to Survivor
    • 年老代(major/full GC)
  • 方法区
    • 永久代

三、内存分配与回收策略

3.1 分配与回收流程

  1. 对象优先放在 Eden,Eden 放不下了就 minor GC,把存活的放入 to Survivor。然后将 to Survivor 和 from Suivivor 互换。(这时 Eden、to Survivor 已经被清空,from Survivor 中存放着上一次 gc 保留的对象。)
  2. 继续把对象放在 Eden 中,Eden 放不下了就 minor GC,把存活的放入 to survivor。把 from survivor 的也放入 to survivor。然后将 to survivor 和 from suivivor 互换。(这时 Eden、to Survivor 已经被清空,from Survivor 中存放着上一次 gc 保留的对象。)

注意:如果 to Survivor 中的空间不足以存放要保留的对象,那对象会被放入老年代,这样的机制称为分配担保

3.2 提高效率

为提高效率,避免在 Eden 和 Survivor 中发生大量的内存复制。虚拟机采取了以下措施:

  1. 大对象直接进入老年代
    大对象不经过新生代,直接进入老年代
  2. 长期存活的对象将进入老年代
    每个对象都有一个年龄,survivor 一次加一。当它的年龄熬到一定岁数,说明以后也很可能不会被回收,于是移到老年代中。
  3. 动态对象年龄判定
    如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,这说明同一时期创造了很多有用的对象,他们的生命周期很可能是一致的,对他们进行复制比较耗时,所以移到老年代中。

3.3 空间分配担保

如上所述,在新手代中进行的 GC 是 minor GC,需要老年代进行分配担保,但当老年代也没有足够的空间去容纳要保留的对象,那就需要进行一次 full GC,清除老年代无用的对象。

猜你喜欢

转载自blog.csdn.net/Gdeer/article/details/88783811