Finalizer对象积压会导致GC效率下降和内存泄漏,需通过GC日志、堆转储分析、线程状态检查及辅助命令综合诊断:关注FinalReference处理耗时、Finalizer线程阻塞、FGC频次增加与老年代持续增长等关键信号。

Finalizer 对象积压是一种隐蔽但高危的内存溢出诱因,它不会在堆直方图中显眼地“占大头”,却会拖慢 GC 回收节奏,让大量本该死亡的对象长期滞留堆中。识别它,关键不是看“谁占得多”,而是看“谁走不了”。
观察 GC 日志中的 Finalizer 线程行为
启动 JVM 时加上以下参数,获取关键线索:
- -XX:+PrintGCDetails -XX:+PrintGCDateStamps:确认 GC 频次与回收效率
-
-XX:+PrintReferenceGC(JDK8+):日志中会出现类似
FinalRef的条目,显示被 Finalizer 队列引用的对象数量 - -XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -Xlog:gc+ref=debug(JDK10+):更精细地看到 F-Queue 入队/出队情况
若日志中反复出现 FinalReference 处理耗时长、或每次 GC 后 FinalRef 数量持续不降甚至上升,就是积压信号。
检查堆转储中不可达但未回收的对象
用 jmap -dump:format=b,file=heap.hprof 获取 dump 后,用 Eclipse MAT 分析:
- 打开 Dominator Tree,筛选类名含
Finalizer或java.lang.ref.Finalizer的项——它本身可能不大,但它的子树往往庞大 - 右键 → Path to GC Roots → with all references,查看哪些自定义类被 Finalizer 持有(即重写了
finalize()) - 特别关注 “Not strongly reachable” 却仍出现在 dominator 中的对象——说明它们已不可达,只因 finalize 未执行而无法释放
验证 Finalizer 线程是否成为瓶颈
运行时直接检查线程状态和队列深度:
- 执行
jstack,查找名为Finalizer的线程,观察其是否处于WAITING或长时间RUNNABLE(尤其在调用用户finalize()方法内) - 用
jstat -gc查看FGC(Full GC)次数是否陡增,同时OU(Old Gen 使用量)缓慢但持续上涨——这是 Finalizer 拖累 GC 的典型曲线 - 若应用中有自定义
finalize()方法,检查其内部是否有 I/O、锁等待、远程调用等阻塞操作,这类代码会让 Finalizer 线程“卡住”
辅助命令快速筛查
无需 dump 堆,也能初步判断:
-
jmap -histo:看| grep -i finalizer java.lang.ref.Finalizer实例数是否异常高(数千以上需警惕) -
jstat -gc:连续 5 次采样,对比1000 5 YGC/FGC间隔与OU增速;若 FGC 越来越密但OU下降极少,Finalizer 积压可能性大 - 用
jcmd(配合VM.native_memory summary -XX:NativeMemoryTracking=detail)排除堆外内存干扰,聚焦堆内问题
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











