问: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的垃圾回收机制有助于优化应用性能、解决内存泄漏等问题。

相关推荐
请你打开电视看看1 小时前
Jvm知识点
jvm
程序猿进阶2 小时前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
阿龟在奔跑13 小时前
引用类型的局部变量线程安全问题分析——以多线程对方法局部变量List类型对象实例的add、remove操作为例
java·jvm·安全·list
王佑辉14 小时前
【jvm】方法区常用参数有哪些
jvm
王佑辉14 小时前
【jvm】HotSpot中方法区的演进
jvm
Domain-zhuo14 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式
Theodore_10222 天前
7 设计模式原则之合成复用原则
java·开发语言·jvm·设计模式·java-ee·合成复用原则
我是苏苏2 天前
Web开发:ORM框架之使用Freesql的DbFrist封装常见功能
java·前端·jvm
天草二十六_简村人2 天前
Java语言编程,通过阿里云mongo数据库监控实现数据库的连接池优化
java·jvm·数据库·mongodb·阿里云·微服务·云计算
老码沉思录2 天前
Android开发实战班 - 数据持久化 - Room 数据库应用
android·jvm·数据库