Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

  • JVM

    • JVM概述
    • JVM基础

    • 类加载机制

    • 运行时内存

    • 垃圾回收

      • 垃圾回收概述
      • 可达性分析与引用类型
        • 1. 引用计数的问题
        • 2. 可达性分析
        • 3. 常见 GC Roots
        • 4. 强引用
        • 5. 软引用
        • 6. 弱引用
        • 7. 虚引用
        • 8. finalization
        • 9. 阶段小结
        • 10. 可达性分析图解
        • 11. 四种引用对比表
        • 12. ThreadLocal 为什么容易泄漏
        • 13. Tips 快问快答
      • 垃圾回收算法
      • 分代收集与对象晋升
      • 常见垃圾回收器
      • G1垃圾回收器
      • ZGC与低延迟GC
      • GC日志分析
    • 执行引擎与优化

    • 工具与问题排查

    • 调优实践与面试

目录

可达性分析与引用类型

垃圾回收的第一步是判断对象是否还活着。JVM 主要通过可达性分析完成这件事。

# 1. 引用计数的问题

引用计数思路很简单:对象被引用一次,计数加一;引用失效,计数减一。计数为零就可以回收。

问题是无法很好处理循环引用。

class Node {
    Node next;
}

Node a = new Node();
Node b = new Node();
a.next = b;
b.next = a;
a = null;
b = null;

a 和 b 互相引用,但外部已经无法访问它们。如果只看引用计数,它们的计数不为零,可能无法回收。

# 2. 可达性分析

可达性分析从 GC Roots 出发,沿引用关系向外搜索。

GC Roots -> 对象A -> 对象B -> 对象C

能被搜索到的对象是可达对象,不能被搜索到的对象可以被回收。

这种方式可以解决循环引用问题。

# 3. 常见 GC Roots

常见 GC Roots 包括:

  • 栈帧局部变量表中的引用。
  • 方法区中的静态变量引用。
  • 方法区中的常量引用。
  • JNI 本地引用。
  • 活跃线程。
  • JVM 内部对象。
  • 被同步锁持有的对象。

排查内存泄漏时,核心就是找到对象为什么还能从 GC Roots 到达。

# 4. 强引用

强引用是最常见引用。

User user = new User();

只要强引用还存在,对象就不会被 GC 回收。

集合、缓存、静态变量中的引用如果没有清理,最容易造成内存泄漏。

# 5. 软引用

软引用用 SoftReference 表示。

SoftReference<byte[]> ref = new SoftReference<>(new byte[1024]);

软引用对象在内存不足时可能被回收,曾经常用于缓存。但现代缓存更推荐使用成熟缓存框架,因为软引用回收时机不够可控。

# 6. 弱引用

弱引用用 WeakReference 表示。

WeakReference<User> ref = new WeakReference<>(new User());

只要发生 GC,弱引用关联对象如果没有强引用,就可能被回收。

典型应用:

  • WeakHashMap。
  • ThreadLocal 的 key。
  • 一些监听器和元数据缓存。

# 7. 虚引用

虚引用用 PhantomReference 表示。

它不能通过 get() 获取对象,主要用于对象被回收时接收通知,配合引用队列管理堆外资源。

直接内存释放相关机制就和虚引用、Cleaner 思想有关。

# 8. finalization

对象真正回收前可能涉及 finalize,但 finalize 已经过时,不推荐使用。

原因:

  • 执行时机不可控。
  • 性能差。
  • 容易导致对象复活。
  • 可能造成资源释放延迟。

资源管理应使用 try-with-resources、显式 close 或 Cleaner 等更可控机制。

# 9. 阶段小结

可达性分析回答“对象为什么还活着”。引用类型回答“引用有多强”。排查内存泄漏时,最重要的是沿 GC Roots 引用链找到强引用来源,而不是只看对象本身占了多少内存。

# 10. 可达性分析图解

GC Roots
├─ 栈中局部变量 refA ──► Object A ──► Object B
├─ 静态变量 cache  ───► Object C ──► Object D
└─ JNI 引用        ───► Object E

Object X ──► Object Y
   ▲
   └── 只有互相引用,但没有从 GC Roots 可达

在这个图里:

  • A、B、C、D、E 都可达,不能回收。
  • X、Y 虽然互相引用,但不可从 GC Roots 到达,可以回收。

# 11. 四种引用对比表

引用类型 回收行为 典型用途 注意点
强引用 只要可达就不回收 普通对象引用 最容易造成泄漏
软引用 内存不足时可能回收 历史上用于缓存 回收时机不可控
弱引用 下次 GC 即可回收 WeakHashMap、ThreadLocal key value 仍可能泄漏
虚引用 无法直接获取对象 回收通知、资源清理 必须配合 ReferenceQueue

# 12. ThreadLocal 为什么容易泄漏

ThreadLocalMap 的 key 是弱引用,但 value 是强引用。

Thread
 └─ ThreadLocalMap
    └─ Entry
       ├─ key: WeakReference&lt;ThreadLocal>
       └─ value: Object strong reference

如果 ThreadLocal key 被回收,但线程长期存活,value 可能仍被 Entry 持有,造成泄漏。

正确写法:

try {
    threadLocal.set(value);
    // business
} finally {
    threadLocal.remove();
}

# 13. Tips 快问快答

Q:循环引用一定不能回收吗?

A:能回收。只要循环中的对象不可从 GC Roots 到达,就可以被可达性分析识别为垃圾。

Q:弱引用对象什么时候回收?

A:当对象只被弱引用关联时,下一次 GC 就可能回收。

Q:软引用适合做缓存吗?

A:现代业务更推荐 Caffeine 这类明确容量和淘汰策略的缓存,软引用缓存不够可控。

Q:内存泄漏分析为什么看 GC Roots?

A:因为泄漏对象之所以不被回收,一定存在从 GC Roots 到它的引用链。

上次更新: 2026/06/24, 16:03:01
垃圾回收概述
垃圾回收算法

← 垃圾回收概述 垃圾回收算法→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式