社交媒体

垃圾回收

垃圾回收

钩子:C 语言程序员的"忘 free 恐惧"

写 C 语言的人有一条铁律:malloc 申请的内存,用完必须自己 free。忘了?内存泄漏,程序越跑越肥,直到哪天崩给你看。

而 Java 从第一篇到现在,你 new 了成千上万个对象,一次 free 都没写过,程序也没见越跑越肥。

凭什么?因为 JVM 雇了一位"保洁阿姨"——GC(垃圾回收器,Garbage Collector)。你只管 new,她负责在你不知道的时候,把没用的对象清走。

今天的任务就三件事:她怎么知道谁是垃圾?她什么时候来打扫?以及一个反直觉的真相——Java 也有内存泄漏。

核心:GC 怎么判断谁是垃圾

很多人以为 GC 数引用:一个对象被引用 1 次、2 次……归零就回收。不是这样的。

Java 用的是可达性分析:从一群"根"(GC Roots)出发——

  • 栈里的局部变量
  • 静态变量
  • 常量池引用……

顺着引用链一路爬。能爬到的对象是活人;爬不到的,管你引用数是几,都是垃圾。

为什么不用引用计数?因为"你引用我、我引用你"的两个废弃对象,引用数永远不是 0,但它们已经没有任何用处了。可达性分析一爬,两个都到不了根,双双回收——没有漏洞。

35-GC可达性分析图.png

实验:我造了 30MB 垃圾,看她来不来收

demo 里做了两组对照实验:

实验 1:断开引用,请求 GC

static void heapTrash() {
    byte[] trash = new byte[1024 * 1024];   // 1MB 局部变量
}

方法一结束,trash 这个引用就出栈了,1MB 对象失去"根"。连造 30 次再调 System.gc():

堆里现有活对象约: 88 MB
System.gc() 之后 :  6 MB
释放了约 81 MB —— 没根的对象被清走了

保洁阿姨真的来收了。

⚠️ 这几个 MB 数每次跑都会浮动(堆里还躺着别的活对象,GC 什么时候来也不固定):你在自己机器上跑,可能是 80 → 10,也可能是别的数。**只要看方向——大幅回落,结论就成立。**下面第二组实验同理。

实验 2:被 static 抓住的对象,她也收不走

把同样的 30MB 塞进一个 static 集合(静态变量是 GC Root),再 gc:

static 集合抓着 30MB,gc 后仍约: 66 MB(降不下来)

只要还有根引用着,对象就不是垃圾。 static 集合只进不出,就是 Java 版内存泄漏——你以为是垃圾,GC 认为它是活人。

关于 System.gc() 还要泼一盆冷水:它只是"建议",JVM 听不听随缘。生产代码里不要指望它,也不要乱调它。

速查表

要点 内容
GC 是什么 JVM 的自动内存保洁,new 完不用 free
回收标准 可达性分析:从 GC Roots 顺藤摸瓜
易错点 回收标准不是引用计数,互指的垃圾照收
System.gc() 只是建议,不保证立即执行
Java 内存泄漏 该断的引用不断(static 集合只进不出)
下篇 36 篇全景地图,全系列收官

本文示例代码均已本地实测通过,可直接运行。
对应文件:GcDemo.java


橙码酱,分享更有用的技术知识,助你成为更优秀的开发者。