Java11应用Xmx设256M却占用超500M,Finalizer类占比高如何排查
问题排查步骤
1. 先确认JVM参数实际生效情况
你当前的启动脚本存在参数位置错误:java命令规定所有JVM参数必须放在-jar参数之前,-jar之后的所有内容都会被识别为应用本身的入参,你脚本里的-Xdebug、-XX:+HeapDumpOnOutOfMemoryError等参数都放在了-jar后面,大概率没有生效。
执行以下命令确认运行中进程的实际生效参数:jinfo -flags <你的进程PID>
如果参数确实不生效,调整启动脚本参数顺序即可。
2. 排查Finalizer队列堆积根源
MAT报告显示java.lang.ref.Finalizer占比高,说明大量需要执行finalize()方法的对象堆积在Finalizer队列中无法被回收,按以下步骤定位:
- 打开MAT的支配树(Dominator Tree)视图,查看
Finalizer引用指向的具体对象类型,确认是哪类实例被大量积压,通常是IO流、数据库连接、Socket连接、第三方资源类等对象,这类类会内置finalize()方法做资源兜底释放,不需要你自己实现。 - 执行
jstack <你的进程PID>抓取线程栈,找到名为Finalizer的守护线程,查看它的当前执行栈,确认是否卡在某个类的finalize()方法上,是否存在锁竞争、IO阻塞等导致线程挂起的情况。 - 可添加JVM启动参数
-XX:+PrintFinalizer,打印所有Finalizer执行日志,统计执行耗时最长的类。
3. 排查总内存超配原因
-Xmx只限制Java堆内存的最大值,JVM总占用内存还包含堆外内存、元空间、线程栈、JIT缓存、JNI调用占用的native内存等,超配排查步骤:
- 添加JVM启动参数
-XX:NativeMemoryTracking=summary,重启后执行jcmd <你的进程PID> VM.native_memory summary,查看各部分内存占用分布,确认是否是堆外内存、元空间等部分占用过高。 - 如果用到了Netty、OkHttp等大量使用堆外内存的框架,检查是否存在堆外内存未主动释放的泄漏问题。
- 检查应用创建的线程数,每个线程默认栈大小为1M,线程数过多也会占用大量内存。
4. 修复建议
- 找到积压的资源类后,检查业务代码是否在使用完资源后没有主动调用
close()方法,尽量用try-with-resources语法自动释放资源,减少对finalize()兜底的依赖。 - 如果确认是Finalizer线程优先级过低导致回收速度赶不上创建速度,可临时添加参数
-XX:FinalizerPriority=10提高Finalizer线程优先级,该方案仅为临时止血,必须根治资源未主动释放的问题。 - 避免使用内置了
finalize()方法的过时类,这类实现早已在JDK新版本中被标记为废弃,性能差且容易导致内存积压。
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

