我们在Windows服务器上使用Tomcat 8.5并遇到因使用网络套接字而导致的内存泄漏。服务器Web套接字如下所示:
package ad.ecs.async.websocket;
import java.io.IOException;
import java.util.Arrays;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import ad.common.Global;
import ad.ecs.async.AsyncEngine;
import ad.ecs.async.AsyncResponse;
import ad.ecs.async.AsyncType;
import ad.ecs.db.DatabaseEngine;
import ad.ecs.db.paradox.User;
import ad.ecs.security.engine.SecurityEngine;
/**
* @author Serge Perepel
* @since Aug 23, 2017 12:23:32 PM
*/
@ServerEndpoint(value = "/asyncMsg", encoders = AsyncResponseEncoder.class)
public class ECSAsync {
@OnOpen
public void open(Session session) throws IOException{
session.getBasicRemote().sendText("Connection Established");
}
@OnMessage
public String login(String sessionID, Session session) {
AsyncEngine.INSTANCE.wsConnect(session, sessionID);
org.hibernate.Session dbSession = DatabaseEngine.getSessionFactory().openSession();
try {
int userID = SecurityEngine.INSTANCE.getUserIDBasedOnSessionID(sessionID);
User user = (User) dbSession.get(User.class, userID);
if (user != null) {
if (user.getNextLogin() == 1) {
AsyncResponse response = new AsyncResponse();
response.setType(AsyncType.None);
response.setData(Arrays.asList("PASSWORD"));
response.setObjData("PASSWORD");
AsyncEngine.INSTANCE.addTransientResult(sessionID, response);
}
}
} finally {
dbSession.close();
}
return "ok";
}
@OnClose
public void close(Session session, CloseReason reason) {
AsyncEngine.INSTANCE.wsDisconnect(session);
}
@OnError
public void error(Session session, Throwable error) {
Global.INSTANCE.getLogHelper().exception(error);
//session.close(new CloseReason(closeCode, reasonPhrase));
}
}
前端打开连接并发送导致无效的sessionID
行AsyncEngine.INSTANCE.wsConnect(session, sessionID);
以抛出异常,然后在前端发生断开连接。此时前端打开新连接,进程进入循环。我假设连接处理程序假设在断开连接后被释放。但它似乎在积累。
以下是Eclipse中Memory Analyzer插件的报告:
One instance of "org.apache.coyote.AbstractProtocol$ConnectionHandler"
loaded by "java.net.URLClassLoader @ 0x720029098" occupies 2,153,196,128
(88.10%) bytes. The memory is accumulated in one instance of
"java.util.concurrent.ConcurrentHashMap$Node[]" loaded by "<system class loader>".
Keywords
org.apache.coyote.AbstractProtocol$ConnectionHandler
java.util.concurrent.ConcurrentHashMap$Node[]
java.net.URLClassLoader @ 0x720029098
这是wsConnect方法:
public synchronized void wsConnect(Session session, String sessionID) {
SecurityEngine.INSTANCE.checkSession(sessionID); // Exception is thrown here
connections.put(sessionID, session);
if (transientResponses.containsKey(sessionID)) {
transientResponses.get(sessionID).values().stream()
.forEach(p -> addTransientResult(sessionID, p));
}
}