复盘:Java Runnable关闭逻辑不完善导致的内存泄漏
前言
配置在边缘节点的端口转发、数据包修改服务软件,发生 周期性 的服务中断。周期介于5~10天之间,尤易在周末发生。推测与流量有关。
中断时报错: Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
内存占用出现逐渐增加的趋势。

应急处理
先发维护通知忽悠用户,然后趁机定期进行重启,清理内存。
肇事代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ServerSocket serverSocket = new ServerSocket(localPort); log.info("localPort="+localPort + ";remoteIp=" + remoteIp + ";remotePort="+remotePort+";启动本地监听端口" + localPort + "成功!"); while(true){ Socket clientSocket = null; Socket remoteServerSocket = null; try { clientSocket = serverSocket.accept(); remoteServerSocket = new Socket(remoteIp ,remotePort); String clientIP = clientSocket.getLocalAddress().toString(); clientIP = clientIP.substring(clientIP.lastIndexOf("/")+1); log.info("客户端IP:"+clientIP); (new TransPortData(clientSocket ,remoteServerSocket ,"1")).start(); (new TransPortData(remoteServerSocket ,clientSocket,"2")).start(); } catch (Exception ex) { log.info("",ex); } }
|
原因分析
首先看Heap堆栈。

从图中可以看出存在大量的byte[],而上面代码中启动数据转换接口的同时,正是使用了一个byte[]变量储存TCP数据包。
1 2 3 4
| InputStream in = getDataSocket.getInputStream() ;
byte[] data = new byte[2048]; int readlen = in.read(data);
|
为了进一步确认这个错误,在启动指令中加入分析功能,反馈如下:

nid达到百万级别,可以认为确实是线程清理不干净导致的。
代码优化
加入 while 1 退出机制
1 2 3 4 5 6 7 8 9
| if(readlen<=0){ TimeoutStamp++; if(TimeoutStamp>=300){ break; } Thread.sleep(100); continue; }
|
并在finally中关闭数据包传输器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| finally{ try { if(putDataSocket != null){ putDataSocket.close(); } } catch (Exception exx) { } try { if(getDataSocket != null){ getDataSocket.close(); } } catch (Exception exx) { } }
|
实测内存占用稳定,不再发生内存泄漏。