GC 回收机制与分代回收策略

垃圾回收(Garbage Collection,简写为 GC)可能是虚拟机众多知识点中最为大众所熟知的一个了,也是Java开发者最关注的一块知识点。Java 语言开发者比 C 语言开发者幸福的地方就在于,我们不需要手动释放对象的内存,JVM 中的垃圾回收器(Garbage Collector)会为我们自动回收。但是这种幸福是有代价的:一旦这种自动化机制出错,我们又不得不去深入理解 GC 回收机制,甚至需要对这些“自动化”的技术实施必要的监控和调节。

上一节课我介绍了 Java 内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈 3 个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作,这几个区域内不需要过多考虑回收的问题。

而堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的就是这部分内存。

什么是垃圾
所谓垃圾就是内存中已经没有用的对象。 既然是”垃圾回收",那就必须知道哪些对象是垃圾。Java 虚拟机中使用一种叫作"**可达性分析”**的算法来决定对象是否可以被回收。

可达性分析
可达性分析算法是从离散数学中的图论引入的,JVM 把内存中所有的对象之间的引用关系看作一张图,通过一组名为”GC Root"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,最后通过判断对象的引用链是否可达来决定对象是否可以被回收。如下图所示:

比如上图中,对象A/B/C/D/E 与 GC Root 之间都存在一条直接或者间接的引用链,这也代表它们与 GC Root 之间是可达的,因此它们是不能被 GC 回收掉的。而对象M和K虽然被对J 引用到,但是并不存在一条引用链连接它们与 GC Root,所以当 GC 进行垃圾回收时,只要遍历到 J/K/M 这 3 个对象,就会将它们回收。

注意:上图中圆形图标虽然标记的是对象,但实际上代表的是此对象在内存中的引用。包括 GC Root 也是一组引用而并非对象。

GC Root 对象
在 Java 中,有以下几种对象可以作为 GC Root:

Java 虚拟机栈(局部变量表)中的引用的对象。
方法区中静态引用指向的对象。
仍处于存活状态中的线程对象。
Native 方法中 JNI 引用的对象。
什么时候回收
不同的虚拟机实现有着不同的 GC 实现机制,但是一般情况下每一种 GC 实现都会在以下两种情况下触发垃圾回收。

Allocation Failure:在堆内存中分配时,如果因为可用剩余空间不足导致对象内存分配失败,这时系统会触发一次 GC。
System.gc():在应用层,Java 开发工程师可以主动调用此 API 来请求一次 GC。
代码验证 GC Root 的几种情况
现在我们了解了 Java 中的 GC Root,以及何时触发 GC,接下来就通过几个案例来验证 GC Root 的情况。在看具体代码之前,我们先了解一个执行 Java 命令时的参数。

-Xms 初始分配 JVM 运行时的内存大小,如果不指定默认为物理内存的 1/64。

比如我们运行如下命令执行 HelloWorld 程序,从物理内存中分配出 200M 空间分配给 JVM 内存。

复制java -Xms200m HelloWorld
验证虚拟机栈(栈帧中的局部变量)中引用的对象作为 GC Root
我们运行如下代码:

复制public class GCRootLocalVariable {
    private int _10MB = 10 * 1024 * 1024;
    private byte[] memory = new byte[8 * _10MB];

    public static void main(String[] args){
        System.out.println("开始时:");
        printMemory();
        method();
        System.gc();
        System.out.println("第二次GC完成");
        printMemory();
    }

    public static void method() {
        GCRootLocalVariable g = new GCRootLocalVariable();
        System.gc();
        System.out.println("第一次GC完成");
        printMemory();
    }

    /**
     * 打印出当前JVM剩余空间和总的空间大小
     */
    public static void printMemory() {
        System.out.print("free is " + Runtime.getRuntime().freeMemory()/1024/1024 + " M, ");
        System.out.println("total is " + Runtime.getRuntime().totalMemory()/1024/1024 + " M, ");
    }
}
打印日志:

复制开始时:
free is 242 M, total is 245 M,
第一次GC完成
free is 163 M, total is 245 M,
第二次GC完成
free is 243 M, total is 245 M,
可以看出:

当第一次 GC 时,g 作为局部变量,引用了 new 出的对象(80M),并且它作为 GC Roots,在 GC 后并不会被 GC 回收。
当第二次 GC:method() 方法执行完后,局部变量 g 跟随方法消失,不再有引用类型指向该 80M 对象

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM(Java虚拟机)的垃圾回收GC机制是自动管理内存的一种机制。它通过自动识别和回收不再使用的对象来释放内存空间,以避免内存泄漏和程序中的内存溢出错误。 JVM中的垃圾回收器负责执行垃圾回收。当对象不再被引用时,垃圾回收器将标记这些对象为垃圾,并将它们从内存中回收。以下是JVM GC回收机制的一般过程: 1. 标记阶段(Marking Phase):从根对象(如线程栈中的引用、静态变量等)开始,垃圾回收器将遍历对象图并标记可达的对象。 2. 清除阶段(Sweeping Phase):垃圾回收器将清除标记为垃圾的对象,并回收它们占用的内存空间。 3. 压缩阶段(Compacting Phase):如果需要,垃圾回收器会对存活的对象进行整理,使它们在内存中连续排列,从而减少碎片化并提高内存的利用率。 4. 再分配阶段(Allocation Phase):如果需要,垃圾回收器会为新对象分配内存空间。 具体的垃圾回收算法和策略可能因不同的JVM实现而异。常见的垃圾回收算法包括标记-清除(Mark and Sweep)、复制(Copying)、标记-整理(Mark and Compact)等。JVM还提供了不同的垃圾回收器,如Serial、Parallel、CMS(Concurrent Mark Sweep)、G1(Garbage-First)等,以满足不同场景下的需求。 总而言之,JVM的垃圾回收机制通过自动识别和回收不再使用的对象来管理内存,确保程序运行时的内存使用效率和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值