要判断finalize是否拖慢gc,需主动检查:gc日志中“finalize”统计、jstack查看finalizer线程状态、jfr分析finalizerthread事件耗时与排队长度、静态扫描代码及依赖库中finalize使用。

要分析 finalize 方法是否正在拖慢垃圾回收,关键不是等它出问题再查,而是主动确认它是否存在、是否被触发、以及它在系统中实际造成多大压力。核心思路是:从 JVM 启动参数入手观察日志,用运行时工具定位队列积压和线程行为,再结合代码扫描排除误用。
看 GC 日志里有没有 “Finalize” 相关统计
启动 Java 应用时加上这两个参数:
- -XX:+PrintGCDetails:输出每次 GC 的详细信息
- -XX:+PrintReferenceGC:专门打印软/弱/虚/终结器引用的处理情况
运行一段时间后,检查日志中是否出现类似这样的行:
Finalize: 12345 objects, 0.8ms只要看到 “Finalize” 字样且对象数量不为 0,就说明有类重写了 finalize(),JVM 正在维护 F-Queue 并调度 Finalizer 线程。数字越大、耗时越长,说明压力越明显。
用 jcmd 查 Finalizer 线程状态
对运行中的进程执行:
jcmd或更直接地查看线程栈:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
如果看到 Finalizer 线程长时间处于 RUNNABLE 或 BLOCKED 状态,尤其是堆栈里卡在某个对象的 finalize() 方法内(比如 close() 调用阻塞、IO 等待),就是它拖慢 GC 的直接证据。注意:Finalizer 线程默认优先级很低,容易被其他线程抢占,导致队列堆积。
用 JFR 录制并筛选 Finalizer 事件
JDK 8u262+ 和所有 JDK 11+ 都支持 Java Flight Recorder。启动时加参数:
-XX:+FlightRecorder -XX:StartFlightRecording=duration=60s,filename=recording.jfr录制结束后用 JDK 自带的 jfr 命令或 VisualVM 打开,筛选事件类型:
- jdk.FinalizerThread:看执行耗时、调用次数、排队长度
- jdk.GCPhasePause + 关联 reference processing 阶段:对比有无 Finalizer 对象时的暂停差异
如果 FinalizerThread 单次执行超过 10ms,或平均排队对象数持续 > 100,说明已构成显著延迟风险。
静态扫描代码和依赖库
日志和工具只能告诉你“有影响”,但不能告诉你“谁写的”。需要主动排查:
- 用 SpotBugs(配合 findsecbugs 插件)扫描整个工程,启用规则 FI_FINALIZER_ONLY_NULLS_FIELDS 和 FI_EMPTY_FINALIZER
- 检查 第三方 jar:特别是老版本的 commons-io、netty、hadoop-client、旧版 JDBC 驱动——它们曾大量使用 finalize 清理 DirectBuffer 或 Socket
- 搜索项目中所有 protected void finalize() 的定义,包括空实现(哪怕只写了一行 super.finalize())
发现即删。空 finalize() 比有逻辑的更危险——它让对象变成 finalizable,却什么也不做,纯属增加 GC 周期负担。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










