JVM 一月 05, 2025

arthas 异常排查(一) 一次内存泄露

文章字数 3.6k 阅读约需 3 mins. 阅读次数 0

arthas 异常排查(一 ) 一次内存泄露

1. 查看服务发现内存消耗严重

图中的5638进程java程序内存已经来到了52%,即8G.肯定是不正常的.

top

2. 下载arthas来分析数据.

curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar 
# 选择5638进程的java程序.

arthas启动

3. 使用memory查看内存情况

使用memory 查看内存分布情况.

memory

发现nonheap(非堆内存)存在异常占用.且metaspace(元空间)中占用也异常.由此推测是类加载器泄露或动态生成过多类.

4. 通过jvm查看

使用jvm查看内存分布情况.

jvm

发现类加载统计次数非常高.属于异常情况.

5. 使用classloader查看类加载详情

image-20250106104728660

通过连续的键入classloader.发现com.alibaba.fastjson2.util.DynamicClassLoader这个类加载器loaded次数持续上涨.

使用classloader --list 可以查看类加载器的hash值,用于后续的过滤.

6. 检查代码,找出fastjson相关代码.做测试排查

最后检查到serializeConfig这里存在问题.config只需存于一份即可,无须每次调用都创建一次.

// others
SerializeConfig serializeConfig = new SerializeConfig();
// 驼峰转下划线
serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
String reqStr = JSON.toJSONString(req, serializeConfig);

跟踪serializeConfig代码发现创建对象时会在无参构造中创建ObjectWriterProvider对象.

 public class SerializeConfig {
  .......
    public SerializeConfig() {
        this(new ObjectWriterProvider());
    }

    public SerializeConfig(ObjectWriterProvider provider) {
        this.fieldBased = false;
        this.provider = provider;
    }
}

而在翻阅fastjson的github仓库的时候,fastjson的开发人员回答的是provider单例.

image-20250107145159436

7. 验证

通过通过sc命令查看类加载情况

查找 com.alibaba.fastjson2包下的类.并通过类加载器过滤该类所创建的类.

sc -d com.alibaba.fastjson2.* -c 21d2b7f7 -n 20

发现存在大量的writer类使用后没有销毁.

image-20250106231235402

8. 修改

将SerializeConfig对象上升到类变量中.保持单例.

private SerializeConfig serializeConfig = new SerializeConfig();

......
// 驼峰转下划线
serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
String reqStr = JSON.toJSONString(req, serializeConfig);
0%