记录一次排查堆溢出的流程

发表于
4

背景:测试环境某个微服务,经常会宕机,排查日志显示java.lang.OutOfMemoryError: Java heap space

问题排查流程

  1. 由于报错前后没有明显的异常报错日志,无法直接通过日志看出是什么地方导致堆溢出了,而且启动的时候没有自动在堆溢出的时候dump JVM快照,所以加上了-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx/heapdump.hprof -XX:+ExitOnOutOfMemoryError

  2. 等待不到一天,就又宕机了,这个时候,在/xxx/heapdump.hprof 找到了对应堆dump文件(大小有3.2G),通过JumpServer堡垒机下载到本地。

  3. 使用MAT(Eclipse Memory Analyzer Tool)打开这个堆文件,在Leak Suspects Report 中只有一个Problem Suspect,并且占用空间2G左右,一个Map占用了将近2G的内存。

  4. Leak Suspects Report 的内容,复制给Claude Code,并明确指出项目目录,让AI帮忙排查。

  5. Claude Code反馈需要更多信息,需要排查Map的key和value是什么,以及具体的类,然后就是反复复制MAT的内容给Claude Code(说实话,AI是真的好用)

  6. 然后排查出出问题的类之后(HttpClientFactory),给AI指明本地Maven repo的目录,让AI排查是哪个Jar包引用的这个类导致的问题。

  7. 最后发现是Cybersource的SDK导致的,详情见Github的issue。本质就是Cybersource某个对象没有覆盖hashCode方法和equals方法,导致作为Map的key的时候,没有将多个逻辑上相同的对象,放在同一个key里面(hashCode() 决定“放在哪个桶”,equals() 决定“是不是同一个 key”)。

解决方案(选择其一即可)

  1. 自定义ApiClient缓存,根据不同merchant进行缓存,需要用的时候,再实时通过缓存获取。

  2. fork Cybersource的sdk,然后修复这个问题(补充上hashCode和equals方法)之后重新打包(取一个不同的版本号,比如加上前缀/后缀),项目中引用fork后改动的这个jar包。


0
上一篇 [LeetCode]Subsets