前言

配置在边缘节点的端口转发、数据包修改服务软件,发生 周期性 的服务中断。周期介于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堆栈。
VisualVM截图

从图中可以看出存在大量的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{
//关闭socket
try {
if(putDataSocket != null){
putDataSocket.close();
}
} catch (Exception exx) {
}
try {
if(getDataSocket != null){
getDataSocket.close();
}
} catch (Exception exx) {
}
}

实测内存占用稳定,不再发生内存泄漏。


© 2025 MikeWu597 使用 Stellar 创建

琼ICP备2023004663号-3
湘公网安备 43010302001556号