JVM的内存回收机制——GC

本文介绍JVM垃圾回收机制,包括判断垃圾回收对象的计数收集器和跟踪收集器,阐述何时执行GC,采用分代回收,年轻代和老年代有不同回收规则。还说明了执行GC的复制、标记 - 清除、标记 - 压缩三种方式,最后总结了对象分配和回收特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


JVM通过GC回收堆和方法区的内存,这个过程是自动执行的。我们需要考虑三件事

一、哪些内存需要回收
二、什么时候需要执行GC
三、如何执行GC

一、判断垃圾回收的对象

1.1计数收集器
当计数器为0时,此对象不再被引用可以回收。
ObjectA释放了对ObjectB的引用后,ObjectB的引用计数器变为0,此时可回收ObjectB所占有的内存。
在这里插入图片描述

1.2跟踪收集器
采用的为集中式的管理方式,会全局记录数据引用的状态。有三种实现方式:复制,标记-清除,标记-清除-压缩

二、什么时候需要执行GC

如果只是采用标记-清除-压缩的方式回收,那么随着对象的增加扫描和移动消耗的时间会越来越久,内存回收越来越慢,显然这样是不能满足我们的需求的,所以JVM采用分代回收的方式,将堆区分为年轻代和老年代。

1.在初始阶段新建对象会被分配到Eden区,survivor的区间是空的
在这里插入图片描述
2.当Eden区满的时候,minor gc会被触发,经扫描和标记存活的对象被复制到S0,不存活的对象被回收
在这里插入图片描述
3.第二次Minor GC被触发的时候,S0的数据被复制到S1中,其中对象的年龄加1,Eden中存活的数据也被复制到S1中,S0和Eden被清空,这样S1就存放着年龄不一样的对象

在这里插入图片描述
4.下一次Minor GC则重复这个过程,每次S0 和S1两个区对换
在这里插入图片描述
5.再经过几次Minor GC之后,当存活对象的年龄达到一个阈值之后(可通过参数配置,默认是8),就会被从年轻代Promotion到老年代。
在这里插入图片描述
6.随着MinorGC一次又一次的进行,不断会有新的对象被promote到老年代。 最终,MajorGC将会在老年代发生,老年代的空间将会被清除和压缩。
在这里插入图片描述
什么样的对象可以进入老年代???
1)在年轻代中如果一个对象年龄达到阈值,就会移动
2)Survivor中相同年龄的对象大小超过survivor空间一般,那么不小于这个年龄的对象会进入老年代
3)创建的对象的大小超过阈值,这个对象会直接进入老年代
4)年轻代中大于survivor空间的对象,会移动到老年代

三、如何执行GC

复制
将根集合中回收后依然存活的对象复制到一块新的完全未被使用的空间在这里插入图片描述
标记-清除
将根集合中存活的对象标记,清除未标记的对象,在这个过程中不需要进行对象的移动,会造成内存碎片
在这里插入图片描述
标记-压缩
与标记清除类似,只是在处理结束后会将存活对象内存移动,将内存碎片集中在一起使用
在这里插入图片描述

四、小结

Eden区是连续的空间,S0和S1总有一个为空,S0和S1总是进行角色的交换,这种回收方式是“停止-复制”清理发,对于大部分对象存活周期很短的情况下比较适用。而老年代采用的方式是标记-压缩算法。如果年轻代过小,容易导致GC频繁地发生,增大系统消耗,这样很多大文件会直接进入老年代,容易诱发Full GC,如果老年代过小,更容易诱发Full GC。
综上:1.对象会优先在Eden上分配
2.大对象直接进入老年代
3.长期存活的对象进入老年代
本篇博客主要用于知识的总结,是模仿该博客写的,大家可以区查看原博主,写的非常详细
https://blog.youkuaiyun.com/suifeng3051/article/details/48292193

### JVM 内存模型 JVM 内存模型由多个区域组成,这些区域各自承担不同的职责以支持 Java 应用程序的高效运行。主要组成部分包括: - **线程私有区**:这部分内存为每个线程独享,主要包括程序计数器、虚拟机栈以及本地方法栈。其中,程序计数器用于记录当前线程所执行字节码指令的位置;而虚拟机栈则保存着局部变量表、操作数栈等信息,每调用一次方法就会创建一个新的栈帧并压入此栈中[^1]。 - **堆 (Heap)**:这是所有线程共享的一块大型连续内存空间,主要用于存放对象实例及其数组。每当通过 `new` 关键字或其他方式创建新对象时,都会在此处分配相应的存储位置。由于其重要性和复杂度,在这里实现了多种高效的垃圾收集算法来自动管理不再使用的对象资源[^3]。 - **方法区 / 元空间 (Metaspace)**:同样属于全局共享部分,用来储存已被加载到 JVM 中的各种 Class 文件结构描述符(如字段定义、方法签名)、静态变量以及其他常量池条目等内容。值得注意的是,自 JDK 8 开始,原本位于永久代的部分已经被迁移到了原生操作系统进程地址空间内的元空间之中[^2]。 - **直接内存 (Direct Memory)**:虽然严格意义上不属于标准 JVM 运行时数据区之一,但在实际开发过程中却扮演着不可或缺的角色——尤其是在处理 I/O 操作或高性能计算场景下尤为明显。借助 NIO 提供的相关 API 可以绕过常规堆内分配流程直接访问外部物理 RAM 地址,从而减少不必要的上下文切换开销并提升整体吞吐率[^4]。 ### 垃圾回收机制工作原理 为了有效管理和释放无用的对象占用的空间,现代 JVM 实现了一系列复杂的垃圾回收策略。以下是几个常见的 GC 算法概述: #### Serial 收集器 这是一种最基础也是最早期的设计方案,适用于单核 CPU 或者小型应用环境下的年轻代清理任务。整个过程完全串行化完成,暂停应用程序的所有活动直到结束为止。尽管效率较低但对于某些特定场合仍然具有一定的适用价值。 #### Parallel Scavenge 和 ParNew 收集器 两者都是基于复制算法构建而成的新一代并发式解决方案,区别在于前者更注重于最大化吞吐量指标后者则是力求缩短停顿时间长度。它们能够在不影响正常业务逻辑的前提下利用多核心硬件优势加速扫描速度,并且可以根据实际情况动态调整新生代大小比例以便更好地平衡性能表现与响应延迟之间的关系。 #### CMS (Concurrent Mark-Sweep) 收集器 针对老年代设计的一款低延迟能力较强的工具链集合体,采用标记清除的方式逐步识别存活对象的同时允许其他非GC线程继续运作不受干扰。不过缺点也很突出比如容易产生大量碎片化问题影响后续分配动作的成功几率,而且初次全量遍历时仍需短暂冻结整个系统造成一定负面影响。 #### G1 (Garbage First) 收集器 代表目前最先进的综合性技术成果之一,不仅综合考虑到了不同分区之间相互作用特性还特别强调预测性维护理念的应用实践。G1 将整片堆划分为若干固定尺寸的小单元格,每次仅挑选那些预计能获得最大收益的目标区间优先实施整理作业直至达到预期效果为止。这种方式既兼顾了灵活性又不失稳定性,成为当下主流选择趋势所在。 ```java // 示例代码展示如何设置启动参数启用指定类型的垃圾收集器 public class Main { public static void main(String[] args) throws InterruptedException { System.out.println("Starting application with selected garbage collector..."); // 使用 -XX:+UseG1GC 参数开启 G1 Garbage Collector Runtime.getRuntime().exec("java -XX:+UseG1GC MyApplication"); Thread.sleep(5000); // Simulate some work being done System.gc(); // Suggest the VM to perform a full garbage collection cycle } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值