问题现象
- 在进行ESB性能测试时, 在凌晨出现
java.lang.OutOfMemoryError: GC overhead limit exceeded
内存溢出的异常 - 之后所有ESB请求处理时间都变得很久
- 应用占用大量内存
排查过程
- 测试时, 观察到请求数达到约25万次之后会出现该异常
- 观察堆内存发现有1G以
上byte[]
和几百万个MQ相关对象实例 - 用
jmap
手动执行Full GC
之后也没有被回收, 基本可以确定是每次关闭队列连接后相关对象仍被引用无法被销毁 - 最后通过ESB调用链路定位到行方提供IBM MQ封装依赖中, 每次建立连接之后会将会话注册到
MQConnectionShutDownHook
类中的集合中, 这个钩子只有在应用下线时才会调用, 且由于行内IBM MQ是用的短连接, 会产生大量连接对象, 同时依赖很多其他类, 大量请求之后内存中会堆积大量无用对象实例, 且无法被GC - 每次请求约会永久占用4kb以上内存, 25万次请求之后堆内存所剩无几, 最后应用频繁
Full GC
直到发生内存泄漏
解决过程
由于MQConnectionShutDownHook
类访问权限是缺省值, 只能同一包下才能访问, 因此通过类路径强制获取类对象及通过反射获取MQ连接集合后手动清理集合, 之后内存使用正常
经验教训
对于增量较大的集合长期存储在内存中需要谨慎, 应尽量避免或定时清理