垃圾回收与内存管理

一、什么是垃圾回收

标题为 一、什么是垃圾回收

垃圾回收是内存管理的工具,用于回收已失去所有引用的对象占用的内存空间。这些对象被称为“垃圾”

要理解垃圾回收,需要理解以下几个问题::

  1. 哪些内存需要回收?
  2. 什么时候回收?
  3. 如何回收?

二、哪些内存需要回收

标题为 二、哪些内存需要回收

垃圾回收的目标是清除不再使用的内存,但判断何时回收对象却很困难。

2.1、引用计数法

标题为 2.1、引用计数法

引用计数法是一种简单的判断对象是否可回收的方法。每个对象维护一个引用计数器,当对象被其他对象引用时,计数器加1;当对象的引用被删除时,计数器减1。当计数器值为0时,该对象可以被回收。

然而,引用计数法存在缺陷,无法处理循环引用的情况,导致对象无法正确回收。

2.2、可达性分析算法

标题为 2.2、可达性分析算法

为了解决循环引用的问题,又引出了一个新的算法

可达性算法的原理是以一系列叫做 GC Root 的对象为起点出发,引出它们指向的下一个节点,再以下个节点为起点,引出此节点指向的下一个结点。这样通过 GC Root 串成的一条线就叫引用链),直到所有的结点都被访问完毕。如果一个对象没有任何引用链相连,即 GC Root 到这个对象不可达,则证明此对象不可用,可以被回收

现代虚拟机基本都是采用可达性分析算法

GC Roots的对象包括以下几种:

  • 在虚拟机栈中引用的对象,如方法中的参数、局部变量等
  • 在方法区中类静态属性引用的对象
  • 在方法区中常量引用的对象
  • 在本地方法栈中JNI(Native方法)引用的对象
  • Java虚拟机内部的引用,如Class对象、系统类加载器等
  • 所有被同步锁(synchronized关键字)持有的对象
  • 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等

2.2.2、对象自救

标题为 2.2.2、对象自救

Java提供了finalize方法,垃圾回收器在回收对象之前会调用该方法。如果在finalize方法中,对象又重新连接了GC Roots,那么该对象将存活下来。

然而,从Java 9开始,finalize方法已被标记为@Deprecated,不再推荐使用。

2.3、方法区的回收

标题为 2.3、方法区的回收

方法区并不在堆里,但是它也是可以被回收的。方法区的垃圾收集主要回收两部分内容:

  • 废弃的常量
  • 不再使用的类型

一个类型被判定为“不再使用”的条件包括:

  1. 该类所有的实例都已被回收,即Java堆中不存在该类及其任何派生子类的实例。
  2. 加载该类的类加载器已被回收。
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

若满足这三个条件,虚拟机可以对该类型进行回收。

三、垃圾回收算法

标题为 三、垃圾回收算法

常用的垃圾收集器都遵循着一个共同的设计原则,即将Java堆划分为不同的区域,然后将回收对象依据其年龄分配到不同的区域之中存储,这就是分代收集的理论。

分代收集主要划分为新生代老年代,不同区域应用不同的回收策略,使得垃圾回收更高效。

我们需要了解的垃圾回收算法有以下几种:

  • 标记-清除算法
  • 标记-复制算法
  • 标记-整理算法

这些算法的优缺点都是非常明显的

3.2.1、标记-清除算法

标题为 3.2.1、标记-清除算法

该算法最早出现也最基础,正如名字一样,分为两个步骤:标记和清除,标记可以是标记保留的对象也可以是标记可回收的对象

它主要存在两个问题

  • 效率不稳定 - 堆中有大量对象,且大多是要回收的情况下,需要频繁执行标记-清除动作
  • 产生大量内存碎片

3.2.2、标记-复制算法

标题为 3.2.2、标记-复制算法

该算法为了解决·标记-清除算法效率不稳定的问题引入,它的思想是将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉

这个算法解决了效率不稳定的问题,也不会产生内存碎片,但是它引出了新的问题:

  • 存活对象多的情况下,内存复制的开销很大
  • 可用内存缩水到原来的一半

也因此,该算法主要用在新生代存活对象较少的情况下

3.2.3、标记-整理算法

标题为 3.2.3、标记-整理算法

复制算法的特性决定了它不能用在老年代中,针对老年代的特性引入了该方法,标记的阶段和标记-清除算法一样,不同的是,可回收对象并不会直接清理,而是让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存

  1. 什么是垃圾回收?

    • 垃圾回收是内存管理的工具,用于回收已失去所有引用的对象占用的内存空间,这些对象被称为”垃圾”。垃圾回收通过自动处理内存释放,帮助程序员更轻松地管理内存。
  2. 垃圾回收的过程和原理是怎样的?

    • 垃圾回收主要通过判断对象是否可达来确定哪些内存需要回收。最常用的方法是可达性分析算法,以一系列称为”GC Root”的对象为起点,沿着引用链找出所有可达的对象,未被访问到的对象则被认为是垃圾并可以被回收。
    • 引用计数法是另一种简单的回收方法,但因为无法处理循环引用导致的问题,现代虚拟机一般采用可达性分析算法。
    • 方法区也可以进行垃圾回收,主要回收废弃的常量和不再使用的类型。
  3. 垃圾回收算法有哪些?

    • 常见的垃圾回收算法包括标记-清除算法、标记-复制算法和标记-整理算法。
    • 标记-清除算法最早且最基础,分为标记和清除两个步骤,但效率不稳定且会产生大量内存碎片。
    • 标记-复制算法为了解决标记-清除算法的问题而引入,将可用内存划分为两块,每次只使用其中一块,回收时将存活的对象复制到另一块上。
    • 标记-整理算法主要用于老年代,标记的阶段与标记-清除算法相同,不同的是可回收对象不会直接清理,而是让存活的对象向内存一端移动后清理边界以外的内存。