去年升级过一个老的netty3的程序到netty4,近期突然注意到一个问题,就是这个程序随着时间虚拟内存会不断升高.之前升级的时候担心存在内存泄露,所以还特意用jstate跟踪过gc回收的情况,并没有异常.虽然当时也发觉内存占用有缓慢升高的趋势也没有特别在意,仅做观察处理.
由于同机器上还有另一个netty3的老项目可以做对比,所以发觉还是有异常,同时启动的程序虚拟内存占用是老项目的3倍,还没有停止的趋势.
决定还是排查一下,jstack 进程号 > 文件名 ,将堆栈信息dump出一份,观察堆栈信息发现:
"defaultEventExecutorGroup--2" prio=10 tid=0x0000000044191000 nid=0x1df0 waiting on condition [0x0000000043ada000]
java.lang.Thread.State: WAITING (parking)
存在大量以上信息.其中defaultEventExecutorGroup-5-2信息中5为线程组的id,2为线程组中的线程id.然后发现一个特点就是日志中有大量的不同线程组的defaultEventExecutorGroup出现.马上想到一种可能.翻看原始代码中defaultEventExecutorGroup的创建地点:
protected void initChannel(SocketChannel ch) throws Exception {
......
EventExecutorGroup excutor = new DefaultEventExecutorGroup(16); channelHandler = getMessageConnectorHandler();
p.addLast(excutor,"messageConnectorHandler", channelHandler); }
果然当时犯糊涂了,当时想着反正spring是单例,这种初始化方法也就调用一次,局部变量new出来也没什么..但是忘了一件重要的事情就是,这个方法所在的类根本就不是注入的,而是直接new出来的.所以这个局部的DefaultEventExecutorGroup对象每次都是重新创建,导致出现不同线程组的多个线程池出现.
将此线程池提升到静态的成员变量后问题解决:
private final static EventExecutorGroup excutor = new DefaultEventExecutorGroup(16);