类加载器泄漏是metaspace内存泄漏的主因,表现为mu持续增长、full gc频繁及outofmemoryerror;根本原因是类加载器被强引用无法卸载,需结合动态加载行为、部署后内存递增、堆转储中重复classloader等特征综合判断,并通过mat分析gc roots定位静态集合、threadlocal等泄漏源,修复需用weakhashmap、finally清理threadlocal、升级jdbc驱动、规范日志框架生命周期管理。

方法区(Metaspace)中的类加载器泄漏是Java内存泄漏中较隐蔽但影响严重的一类,常表现为Metaspace持续增长、频繁Full GC、甚至java.lang.OutOfMemoryError: Metaspace。根本原因往往是**类加载器被意外强引用而无法卸载,导致其加载的所有类、静态变量、常量池等长期驻留内存**。
识别是否为类加载器泄漏
不能仅凭Metaspace占用高就断定是类加载器泄漏。需结合以下特征综合判断:
- 应用存在动态类加载行为(如OSGi、Spring Boot DevTools、热部署框架、JSP容器、自定义ClassLoader)
- 多次部署/重启后,Metaspace使用量逐次上升,且老年代GC后不回落
-
jstat -gc <pid></pid>显示MU(Metaspace used)持续增长,MC(Metaspace capacity)也缓慢扩大 - 堆转储(heap dump)中发现大量重复的
java.lang.Class、java.lang.reflect.Method或自定义类加载器实例,且它们的ClassLoader字段指向已“废弃”的加载器
从堆转储定位泄漏的类加载器
用Eclipse MAT或JProfiler打开堆转储(建议用jmap -dump:format=b,file=heap.hprof <pid></pid>获取),按如下路径分析:
- 打开 Leak Suspects Report —— MAT常能自动标出被强引用的ClassLoader
- 执行 OQL 查询:
SELECT * FROM java.lang.ClassLoader,按objects数量或retained heap排序,重点关注非AppClassLoader/PlatformClassLoader的实例 - 对可疑ClassLoader右键 → Path to GC Roots(排除弱/软引用),查看谁在持有它 —— 常见泄漏源包括:静态集合(
Map<string classloader></string>)、线程局部变量(ThreadLocal<classloader></classloader>)、未清理的监听器、缓存未失效的Class对象引用
检查常见泄漏模式与修复要点
以下代码结构极易引发类加载器泄漏,需重点审查:
-
静态缓存持有Class或ClassLoader:如
private static final Map<string class>> CLASS_CACHE = new HashMap();</string>—— 改为使用WeakHashMap,或在应用卸载时主动clear -
ThreadLocal未清理:Web容器中线程复用,若Filter/Interceptor将ClassLoader存入ThreadLocal却未remove,该加载器将随线程存活 —— 必须在
finally块中调用threadLocal.remove() -
JDBC驱动未反注册:老版本MySQL驱动(
com.mysql.jdbc.Driver)会在static块中向DriverManager注册,且未提供反注册机制 —— 升级至mysql-connector-java 8.0+(使用com.mysql.cj.jdbc.Driver),或在ContextDestroyed时显式调用DriverManager.deregisterDriver() -
日志框架绑定到旧ClassLoader:Logback/Log4j2在初始化时可能缓存
LoggerContext并关联当前ClassLoader —— 确保使用支持Web应用生命周期的版本,并配置contextSelector或监听ServletContextListener做清理
预防与监控建议
类加载器泄漏重在防范和早期发现:
- 避免在静态上下文中直接持有
ClassLoader、Class或由其加载的任意对象(如Method、Constructor) - 所有动态类加载逻辑,必须配套实现卸载/清理钩子(如
ServletContextListener.contextDestroyed()) - 生产环境启用Metaspace监控:
-XX:MaxMetaspaceSize=512m -XX:MetaspaceSize=256m -XX:+PrintGCDetails,配合Prometheus+JMX采集java.lang:type=MemoryPool,name=Metaspace指标 - 定期用
jcmd <pid> VM.native_memory summary scale=MB</pid>观察native内存中class区域增长趋势,辅助交叉验证
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











