问:JVM当中的垃圾分类怎么搞?

在Java中,JVM(Java虚拟机)的垃圾识别与分类是自动内存管理的重要组成部分。这一过程主要通过垃圾收集器(Garbage Collector)实现,旨在识别和回收不再被程序引用的对象,以释放内存空间。

1. 垃圾识别与分类的基础

在Java中,所谓的"垃圾"是指没有任何引用指向的对象。这些对象虽然仍然占据着内存空间,但已经无法被程序访问和使用。为了识别和回收这些垃圾对象,JVM采用了多种算法和策略。

2. 垃圾识别算法

2.1 引用计数法

引用计数法是一种简单而直观的垃圾收集算法。其核心思想是通过在对象头中添加一个引用计数器,记录该对象被引用的次数。每当有一个新的引用指向该对象时,引用计数加一;当引用被删除或者超出作用范围时,引用计数减一。当引用计数为零时,表示该对象不再被引用,即可以被回收。

然而,引用计数法有一个明显的缺陷,即难以处理循环引用的情况。例如,两个对象互相引用,它们的引用计数永远不会变为零,即使它们已经不再被程序所使用。

示例代码

java 复制代码
class ReferenceCountingObject {
    private int referenceCount = 0;

    public ReferenceCountingObject() {
        // 对象初始化时,引用计数为0
    }

    public void addReference() {
        referenceCount++;
    }

    public void removeReference() {
        referenceCount--;
        if (referenceCount == 0) {
            // 当引用计数为零时,可以进行垃圾回收操作
            System.out.println("对象被回收");
        }
    }
}

public class ReferenceCountingExample {
    public static void main(String[] args) {
        // 创建两个对象
        ReferenceCountingObject obj1 = new ReferenceCountingObject();
        ReferenceCountingObject obj2 = new ReferenceCountingObject();

        // obj1引用计数加一
        obj1.addReference();
        // obj2引用计数加一
        obj2.addReference();

        // obj1引用计数减一
        obj1.removeReference();
        // obj1引用计数为零,可以进行垃圾回收
        // obj2引用计数仍为一
    }
}
2.2 可达性分析算法

可达性分析是Java虚拟机中垃圾收集的核心算法之一。它主要通过判断对象是否能够从一组称为"GC Roots"的根对象出发,通过引用链追踪,最终判断对象是否可达。GC Roots包括虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象以及本地方法栈中JNI(Java Native Interface)引用的对象。

可达性分析的过程如下:

  1. 初始标记阶段:STW(Stop-The-World),只标记GC根直接关联的对象,耗时极少。
  2. 并发标记阶段:由GC根依次向下标记所有关联的对象,可达为存活对象,不可达为可回收对象。
  3. 重新标记阶段:STW,标记新建的对象引用(GC收集器通过读写屏障+增量更新记录新建的引用对象)。
  4. 清除标记阶段:GC线程与用户线程并发清理被标记的垃圾对象。

可达性分析算法能够解决引用计数法无法处理的循环引用问题,因此成为当前主流虚拟机采用的垃圾收集算法。

3. 垃圾分类与回收算法

在识别出垃圾对象后,JVM需要采用合适的算法来回收这些对象所占用的内存空间。以下是几种常见的垃圾回收算法:

3.1 标记-清除算法

标记-清除算法是最基础的收集算法。它分为"标记"和"清除"两个阶段:

  1. 标记阶段:标记出所有存活的对象。
  2. 清除阶段:统一回收所有未被标记的对象。

然而,标记-清除算法存在两个明显的问题:

  1. 效率问题:如果需要标记的对象太多,效率不高。
  2. 空间问题:标记清除后会产生大量不连续的碎片。
3.2 复制算法

复制算法将堆内存分割成两块大小相同的区域,每次只使用其中一块进行对象分配。当这一块内存使用完后,就将还存活的对象复制到另一块区域,然后将已使用的内存空间一次清理掉。

复制算法的优点是不会产生内存碎片,但缺点是内存使用效率低,每次只能使用一半的内存空间。

3.3 标记-整理算法

标记-整理算法是对标记-清除算法的优化。它在标记阶段仍然标记所有存活的对象,但在清除阶段不是直接回收对象,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

3.4 分代收集算法

分代收集算法是当前虚拟机普遍采用的垃圾收集算法。它将堆内存划分为新生代和老年代,根据对象的存活周期选择合适的垃圾收集算法。

  • 新生代:对象存活率低,通常使用复制算法进行回收。新生代被进一步划分为Eden区、From区和To区,默认内存分配比为8:1:1。
  • 老年代:对象存活率高,通常使用标记-清除算法或标记-整理算法进行回收。

4. 垃圾收集器

垃圾收集器是执行垃圾回收操作的组件。在Java中,有多种不同类型的垃圾收集器,包括串行收集器、并行收集器、CMS收集器和G1收集器等。这些收集器采用不同的算法和策略来实现垃圾回收。

4.1 串行收集器(Serial GC)

串行收集器是最基本的垃圾收集器,使用单线程进行垃圾回收。它在进行垃圾收集时,必须暂停其他所有的工作线程,直到收集结束。因此,串行收集器适用于单CPU环境或内存较小的应用。

4.2 并行收集器(Parallel GC)

并行收集器使用多线程进行垃圾回收,提高了回收效率。它同样需要暂停其他工作线程,但回收过程是多线程的,因此适用于多CPU环境或吞吐量要求较高的应用。

4.3 CMS收集器(Concurrent Mark-Sweep GC)

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。它使用并发标记和并发清除的方式来实现垃圾回收,大大减少了停顿时间。然而,CMS收集器在垃圾回收过程中会占用一部分CPU资源,并且可能产生内存碎片。

4.4 G1收集器(Garbage-First GC)

G1收集器是一种面向服务器的垃圾收集器,旨在满足大内存、多处理器的环境下对停顿时间的要求。它将堆内存划分为多个区域(Region),每次只收集一部分区域的垃圾。G1收集器采用并发标记和并发清除的方式,同时结合了复制算法和标记-整理算法的优点,实现了高吞吐量和低停顿时间。

5. 总结

Java JVM中的垃圾识别与分类是自动内存管理的重要组成部分。通过可达性分析算法,JVM能够准确识别出垃圾对象;通过不同的垃圾回收算法和收集器,JVM能够高效地回收这些垃圾对象所占用的内存空间。对于Java开发人员而言,深入理解JVM的垃圾回收机制有助于优化应用性能、解决内存泄漏等问题。

相关推荐
学到头秃的suhian7 小时前
JVM-类加载机制
java·jvm
NEFU AB-IN13 小时前
Prompt Gen Desktop 管理和迭代你的 Prompt!
java·jvm·prompt
唐古乌梁海19 小时前
【Java】JVM 内存区域划分
java·开发语言·jvm
众俗19 小时前
JVM整理
jvm
echoyu.20 小时前
java源代码、字节码、jvm、jit、aot的关系
java·开发语言·jvm·八股
代码栈上的思考1 天前
JVM中内存管理的策略
java·jvm
thginWalker2 天前
深入浅出 Java 虚拟机之进阶部分
jvm
沐浴露z2 天前
【JVM】详解 线程与协程
java·jvm
thginWalker2 天前
深入浅出 Java 虚拟机之实战部分
jvm
程序员卷卷狗3 天前
JVM 调优实战:从线上问题复盘到精细化内存治理
java·开发语言·jvm