记录一次Metaspace内存泄露

举报
牛牛同学 发表于 2022/05/28 17:59:06 2022/05/28
【摘要】 记录一次Metaspace内存泄露排查

问题现象:java程序设置了-Xmx2048m,top命令查看进程实际占用内存达到了8GB,远超设置的堆内存最大值2048m,程序运行正常无明显异常。

定位过程

参考:java内存泄漏分析

1.先确认内存泄露的类型

思路:因程序可正常使用,无明显异常,大概可以排除程序堆内存泄露,如果是堆内泄露日志应该报 java.lang.OutOfMemoryError: Java heap space且程序不应该可以继续使用。

确认下我们的猜想,使用   jcmd ${pid}  VM.native_memory summary scale=MB命令查看下NMT数据

从返回的信息中看出Java Heap使用了2048MB,并未超出-Xmx2048m,但是Class 使用了4888MB,从这儿看出明显是Metaspace发生了内存泄漏。


2.设置-XX:MaxMetaspaceSize

jmap -heap ${pid} 查看下Heap Configuration,从图中能看到java程序的  MaxMetaspaceSize  = 17592186044415 MB,我们知道java元空间在不设置 -XX:MaxMetaspaceSize参数的情况下,是受限于本地内存的大小的,所以发生上面的Class 使用了4888MB的情况。

加上-XX:MaxMetaspaceSize=512m 再看看情况。


3.定位问题代码

加上-XX:MaxMetaspaceSize=512m 后发现进程内存控制住了,但是又有新的问题,发生了java.lang.OutOfMemoryError: Metaspace(见下图)。

那么就不仅仅是Metaspace没回收的问题了,方法区中有无法回收的东西占了内存。方法区的回收主要分为两部分:废弃变量和无用的类。上面看到是Class 使用了4888MB,那先看下进程中加载了哪些类。


JVM参数加上  -XX:+TraceClassLoading -XX:+TraceClassUnloading 查看进程加载类信息,发现程序一直在重复加载mysql-connector-java.jar中的类

使用arthas查看下类的加载路径

再查看下代码发现代码中每次都初始化新的classLoader去动态加载本地jar包


DataMgrClassLoader classLoader = new DataMgrClassLoader(urls.toArray(new URL[] {}), Thread.currentThread().getContextClassLoader());
Thread.currentThread().setContextClassLoader(classLoader);
Class driver = null;
try {
      driver = classLoader.loadClass(context.getParams().get(AgentConsts.JobParams.DBDRIVERNAME));
} catch (ClassNotFoundException e) {
      logger.error("classloader loadclass failed...");
}
logger.info("driver Class: {}", driver);
return null;

调整该段代码,复用classLoader,再做测试,测试通过。


【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。