JVM - 垃圾回收概述
# 1. 概念引入:内存分配与回收的重要性
在 JVM 的运行时数据区中,内存的分配和回收是维持应用程序稳定运行的关键环节,尤其是在 Java 堆和方法区这两个线程共享的区域。本次我们将重点关注垃圾回收 (Garbage Collection, GC) 这一核心机制。
(图中黄色部分标识了内存分配与回收的主要关注区域)
# 2. 什么是垃圾 (Garbage)?
在深入探讨 GC 之前,我们需要明确什么是需要被回收的“垃圾”。
(上图突显了 Java 与 C++ 在内存管理上的一个核心区别:Java 拥有自动垃圾收集技术,而 C/C++ 通常需要手动管理内存)
垃圾收集并非 Java 首创: 自动内存管理和垃圾收集的概念可以追溯到 1960 年的 Lisp 语言。
垃圾收集的核心问题: 围绕垃圾收集,通常有三个经典问题需要回答:
- 哪些内存需要回收? (识别垃圾)
- 什么时候回收? (触发时机)
- 如何回收? (回收算法与策略)
垃圾收集与 Java: 自动垃圾收集机制是 Java 语言的核心特性和主要优势之一,它极大地提高了开发效率,降低了内存泄漏和内存溢出的风险。如今,GC 几乎已成为现代高级语言的标配。然而,Java 的 GC 技术仍在不断发展,以适应不同的硬件设备和应用场景,使其成为 JVM 领域的研究热点和面试重点。
# 2.1 定义“垃圾”
在 Java 的语境下,垃圾 (Garbage) 指的是在程序运行过程中,没有任何存活的引用指针指向的对象实例。换句话说,一个对象如果从所有的 GC Roots (根对象集合,如栈帧中的局部变量、静态变量、JNI 引用等) 出发都无法到达,那么这个对象就被认为是不可达的,即为垃圾。
为何要清理垃圾? 如果不对内存中的垃圾对象进行及时清理,它们所占用的内存空间将无法被后续新创建的对象复用,并会一直保留直到应用程序结束。随着程序的运行,不断产生的垃圾会持续消耗内存资源,最终可能导致内存溢出 (OutOfMemoryError),使应用程序崩溃。
# 2.2 类比:磁盘碎片整理
垃圾回收的过程,除了释放不再使用的内存外,某些 GC 算法(如标记-整理)还包含内存整理 (Compaction) 的步骤,类似于磁盘碎片整理。这可以将存活的对象移动到堆内存的一端,从而腾出大块的连续空闲空间,便于为新对象(尤其是大对象)分配内存。
# 2.3 大厂面试题集锦
(此处列出的面试题是对原文的整理,反映了 GC 在面试中的重要性)
- 蚂蚁金服: 垃圾回收器种类与优缺点 (CMS, G1 重点);GC 算法;G1 回收过程;GC 触发条件;CMS vs G1 特点。
- 百度: GC 算法;分代回收机制;垃圾收集策略。
- 天猫: JVM GC 原理;内存回收方式;CMS 特点;GC 算法与优缺点。
- 滴滴: Java 垃圾回收器种类;G1 应用场景;回收器搭配策略。
- 京东: 垃圾收集器种类与优缺点 (CMS, G1 重点);原理、流程、优缺点;GC 算法实现原理。
- 阿里: 垃圾回收算法;GC 触发条件;如何选择 GC 算法;垃圾回收器种类。
- 字节跳动: 常见 GC 算法与优劣;
System.gc()
/Runtime.gc()
的作用;Java GC 机制;GC Roots;对象回收方式与算法;CMS/G1 原理、解决的问题、回收过程、停顿次数与原因。
# 3. 为什么需要 GC?(The Necessity of GC)
自动垃圾回收对于高级语言至关重要,主要原因如下:
- 防止内存耗尽: 如果只分配内存而不回收,内存资源迟早会被耗尽,导致程序无法继续运行。持续产生垃圾而不清理,最终必然导致内存溢出。
- 内存碎片整理: 即使内存总量足够,频繁分配和回收小对象也可能导致大量内存碎片。没有连续的大块内存,后续的大对象可能无法分配,同样引发问题。GC 的整理阶段可以解决此问题。
- 保证应用程序稳定性: 在复杂、高并发、长时间运行的应用中,手动管理内存极易出错(忘记释放、重复释放、野指针等),导致内存泄漏或程序崩溃。自动 GC 提供了更可靠的内存管理,保障了应用的稳定性。
- 适应复杂业务需求: 随着业务复杂度、用户量和数据量的增长,高效且可靠的内存管理成为必需。没有 GC,现代大型应用几乎无法稳定运行。GC 技术的不断优化(如降低 STW - Stop-The-World 停顿时间)也是为了更好地满足这些需求。
# 4. 早期垃圾回收 (Manual Memory Management)
在 Java 自动 GC 出现之前,以及在 C/C++ 等语言中,内存管理通常是手动进行的。
手动管理方式:
- 开发人员使用
new
(C++) 或malloc
(C) 等关键字/函数显式申请内存。 - 在使用完毕后,需要使用
delete
(C++) 或free
(C) 等关键字/函数显式释放内存。
示例 C++ 代码:
// 示例:手动管理内存
MibBridge *pBridge = new cmBaseGroupBridge(); // 使用 new 申请内存
// ... 使用 pBridge 对象 ...
// 假设某个条件失败,需要释放内存
if (pBridge->Register(kDestroy) != NO_ERROR) {
delete pBridge; // 使用 delete 手动释放 pBridge 指向的内存区域
pBridge = nullptr; // 良好习惯:将指针置空,防止悬垂指针
}
2
3
4
5
6
7
8
9
10
手动管理的优缺点:
- 优点: 开发者可以精确控制内存的分配和释放时机,理论上可以达到最优的内存使用效率。
- 缺点:
- 管理负担重: 开发者需要花费大量精力跟踪内存的分配和释放,容易出错。
- 内存泄漏 (Memory Leak): 如果忘记释放不再使用的内存,该内存将永远无法被再次分配,造成资源浪费,最终可能耗尽内存。
- 悬垂指针/野指针 (Dangling/Wild Pointer): 如果内存被释放后,仍然有指针指向该区域并尝试访问,会导致未定义行为,通常是程序崩溃。
- 重复释放 (Double Free): 对同一块内存释放两次,也可能导致程序崩溃。
自动化 GC 的出现: 为了将开发者从繁重的、易错的手动内存管理中解放出来,自动垃圾回收机制应运而生。有了 GC,上面的 C++ 代码在 Java 中可能简化为:
// Java 示例(假设 MibBridge 是 Java 类)
MibBridge pBridge = new MibBridge();
pBridge.register(kDestroy); // 无需手动 delete,JVM 会在 pBridge 不再被引用时自动回收
2
3
自动内存管理已成为现代高级语言(如 C#, Python, Ruby, Go 等)的标准配置,代表了软件开发的发展趋势。
# 5. Java 垃圾回收机制 (Java GC Mechanism)
Java 的自动垃圾回收是其核心优势之一。
官方 GC 调优指南: https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.html (opens new window)
# 5.1 优点
- 自动化: 无需开发者手动编写分配和回收内存的代码。
- 降低风险: 极大减少了内存泄漏和内存溢出的风险,提高了程序的健壮性。
- 提高开发效率: 让程序员可以更专注于业务逻辑的实现,而不是内存管理的细节。避免了手动管理内存可能引入的各种指针问题。
# 5.2 潜在担忧与学习必要性
尽管自动 GC 非常方便,但也可能带来一些问题:
- 黑盒效应: 如果开发者完全不了解 GC 的工作原理,过度依赖其“自动”特性,当出现内存相关问题时会束手无策。
- 性能瓶颈: GC 操作(特别是 Full GC)通常需要暂停所有用户线程(Stop-The-World, STW),如果 GC 过于频繁或单次暂停时间过长,会严重影响应用程序的响应时间和吞吐量,成为性能瓶颈。
- 定位与调优能力弱化: 不理解 GC 可能导致开发者在遇到
OutOfMemoryError
或性能问题时,难以定位根本原因并进行有效的 JVM 调优。
学习 GC 的重要性:
- 问题排查: 深入理解 GC 原理是排查内存溢出、内存泄漏等问题的基础。
- 性能调优: 了解不同 GC 算法的特点、触发时机、监控指标,才能根据应用特性选择合适的垃圾收集器并进行参数调优,以达到更高的并发量和更好的性能。
- 成为高级开发者: 掌握 JVM 内存管理和 GC 是 Java 高级工程师必备的核心技能。
# 5.3 GC 主要关注的内存区域
JVM 的垃圾收集器主要负责回收堆 (Heap) 和方法区 (Method Area) 中的垃圾。虚拟机栈、本地方法栈、程序计数器是线程私有的,其生命周期与线程绑定,无需 GC介入管理。
重点区域: Java 堆是 GC 工作最主要的区域,绝大多数对象实例都在这里分配和回收。
不同区域的回收频率: 基于分代收集理论(大部分对象朝生夕灭,少数对象长期存活),JVM GC 的回收频率在不同区域有所侧重:
- 频繁收集新生代 (Young Generation): Minor GC 发生频率很高,回收效率也高。
- 较少收集老年代 (Old Generation): Major GC 或 Full GC 频率较低,但耗时较长。
- 基本不收集/很少收集永久代/元空间 (PermGen/Metaspace): 这部分区域的回收(主要是类卸载和常量回收)通常伴随 Full GC 进行,且条件苛刻,频率最低。