使用tomcat上的Web套接字泄漏内存

时间:2018-01-24 19:39:13

标签: java tomcat memory websocket tomcat8

我们在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));
    }
}

0 个答案:

没有答案