背景
接到了线上机器的报警,登上服务器,发现是Java
进程挂了,看日志报了OOM:
java.lang.OutOfMemoryError: Java heap space
问题描述
内存溢出,那当然是看dump文件了。这里推荐大家在产线机器上都加上JVM
参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath={path}/{to}/{dump}
,这样,JVM
在OOM
的时候,会自动做一个内存dump,相当于保存现场。
拿到dump文件,放到MAT里面分析,以下是部分截图:
不看不知道,一看吓一跳:有一个class
(包括其reference
)竟然占据了1.6G的内存!
这个类叫NettyHttpClientService
,是工程里面用来提供异步Http
服务的,其实就是对Netty
做了一层包装。这个类上线已久,之前工作得很好没出什么问题。最近一次上线,也没有对这个类做什么改动,只是新增了一处调用它的地方。
需要进一步挖掘NettyHttpClientService
这个类。
问题分析
继续分析内存占用情况,发现其大头是一个ConcurrentHashMap
,里面的每个node
都占用了10M-20M的空间,而这个Map
里面,已经存在了135个node
:
看来还得去代码里面寻找真相。找到源头,以下是简化版的代码:
public class NettyHttpClientServiceImpl implements NettyHttpClientService, DisposableBean {
// channelPoolMap是一个InetSocketAddress与ChannelPool的映射关系
private AbstractChannelPoolMap<InetSocketAddress, FixedChannelPool> channelPoolMap = new AbstractChannelPoolMap<InetSocketAddress, FixedChannelPool>() {
// 构建新的大小为200的ChannelPool
@Override
protected FixedChannelPool newPool(InetSocketAddress key) {
return new FixedChannelPool(bootstrap.remoteAddress(key), new NettyHttpPoolHandler(), 200);
}
};
// NettyHttpClientService的入口,参数是请求体RequestEntity,成功回调successCallback,失败回调errorCallback
@Override
public Promise<SimpleResponse