记一次排查内存占用逐渐增长问题

本文记录了一次因异步线程池任务堆积导致内存占用异常的排查过程,最终定位到基于hazindvalue实现的并发hashmap中get和revert操作不匹配的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、背景

最近改了好多代码做优化,然后线下测试发现很不符合预期,表现如下:
跑一段时间,内存占用越来越大
一种怀疑内存泄漏,查询了一下午,终于定位,其实是性能回退问题,特别写下mark下

二、排查简述

  1. 任务回收内存是通过异步线程池
  2. 基于监控项,发现异步线程池堆积任务持续增加,因此导致内存增大,不能及时释放
  3. 基于现有监控项+撸了几遍代码,很难找到为啥堆积,继续丰富监控项,详细描述了到底堆积了什么任务,类似如下:
    taskA多少个,taskB多少,taskC多少个。。。
    发现其中A堆积最大,占用总任务个数的百分95。继续看代码,感觉A消费没什么瓶颈点。
  4. 想了半天,灵光一闪,也有少量B和C,是不是因为B和C消费太慢,导致卡住A
  5. 加日志验证,发现确实B和C耗时增大10倍以上
  6. 继续看各个模块代码,定位原因: 使用基于hazind value 实现的并发hash map,本身get和revert应该成对调用,前面有一个模块,只get但是没有revert,导致链越来越长,而回收B和C,会get该数据结构,导致跑的时间越久->get越慢->回收速度越慢->影响其他任务回收->内存占用大

三、小结

少写一行代码浪费了一下午,mark下~~

### 如何使用 `jstat` 诊断和解决 Java 应用内存泄漏 #### 使用 `jstat` 查看垃圾回收统计信息 为了有效利用 `jstat` 工具来识别潜在的内存泄漏问题,可以定期查看应用程序的垃圾收集(GC)行为。命令如下: ```bash jstat -gcutil <pid> 1000 ``` 此命令会每秒打印一次指定进程 `<pid>` 的GC利用率统计数据[^3]。 #### 分析内存增长趋势 如果发现 Full GC 频率逐渐增加或老年代(Old Generation)持续膨胀,则可能存在内存泄漏风险。此时应进一步调查具体对象的增长情况: ```bash jstat -histo <pid> ``` 该指令能够展示当前 JVM 中各类对象实例的数量及其占用空间大小分布状况。 #### 结合其他工具深入分析 仅依靠 `jstat` 可能不足以准确定位具体的泄露源。建议配合使用如 VisualVM 或 MAT(Memory Analyzer Tool),导出并分析 heap dump 文件以找出可疑的大对象或未释放的对象引用链路[^1]。 #### 实施预防措施减少内存泄漏可能性 对于已知容易造成内存泄漏的操作场景采取防范手段非常重要。比如,在操作外部资源时务必采用 try-with-resources 方式自动管理资源生命周期;合理调整 JVM 启动参数控制最大可用内存量等[^2]: ```java // 正确的方式读取文件内容 try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) { String line; while ((line = br.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值