java - Tomcat 8停止Tomcat时出现内存泄漏警告

标签 java tomcat websocket

实现小型微服务架构有以下系统架构:

2 个 Tomcat8 实例在同一台计算机上运行,​​实例 A 在端口 8080 上,实例 B 在端口 8081 上。

它们通过 Websocket-connection (javax.websocket.server.ServerEndpoint) 连接,

InstanceA 是 Websocket-Client,InstanceB 是 WebSocketServer。 实现 ServletContextListener 接口(interface),在 tomcat 启动时调用 contextInitialized(ServletContextEvent arg0) 方法。调用此方法时,我通过

打开与 WebSocketServer 的 Websocket 连接

ContainerProvider.getWebSocketContainer().connectToServer(MyClientEndpoint.class, new URI(arService.getProtocol() + "://"+ arService.getIp() + ":"+ arService.getPort() + arService .getEndPointUrl()));

连接工作得很好,但是当我关闭 InstanceA 时,我得到以下 StackTrace:

WARNING: The web application [AuronGateway] appears to have started a thread named [Thread-5] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 sun.misc.Unsafe.park(Native Method)
 java.util.concurrent.locks.LockSupport.park(Unknown Source)
 java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
 java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
 java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 java.lang.Thread.run(Unknown Source)

奇怪的是,我仅在使用 Oracle JDK 的 Microsoft Windows 上收到此警告(在 Win7 和 Win10 上测试 - 相同的警告)。 当使用 OpenJDK 在 Ubuntu14.04 上部署相同的代码时,我没有收到警告。 我使用 Eclipse 在两个系统上部署 WebApplication 和 Java 8/Tomcat8。

在调试应用程序时,我注意到 websocket-Connection 已正确关闭。

编辑: 在 websocketServer-Side(InstanceB) 上,我在 onError 方法中收到以下异常:

java.io.IOException: Eine vorhandene Verbindung wurde vom Remotehost geschlossen
    at sun.nio.ch.SocketDispatcher.write0(Native Method)
    at sun.nio.ch.SocketDispatcher.write(Unknown Source)
    at sun.nio.ch.IOUtil.writeFromNativeBuffer(Unknown Source)
    at sun.nio.ch.IOUtil.write(Unknown Source)
    at sun.nio.ch.SocketChannelImpl.write(Unknown Source)
    at org.apache.tomcat.util.net.NioChannel.write(NioChannel.java:124)
    at org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:183)
    at org.apache.coyote.http11.upgrade.NioServletOutputStream.doWriteInternal(NioServletOutputStream.java:94)
    at org.apache.coyote.http11.upgrade.NioServletOutputStream.doWrite(NioServletOutputStream.java:61)
    at org.apache.coyote.http11.upgrade.AbstractServletOutputStream.writeInternal(AbstractServletOutputStream.java:165)
    at org.apache.coyote.http11.upgrade.AbstractServletOutputStream.write(AbstractServletOutputStream.java:132)
    at org.apache.tomcat.websocket.server.WsRemoteEndpointImplServer.onWritePossible(WsRemoteEndpointImplServer.java:99)
    at org.apache.tomcat.websocket.server.WsRemoteEndpointImplServer.doWrite(WsRemoteEndpointImplServer.java:80)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.writeMessagePart(WsRemoteEndpointImplBase.java:452)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessage(WsRemoteEndpointImplBase.java:340)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessageBlock(WsRemoteEndpointImplBase.java:272)
    at org.apache.tomcat.websocket.WsSession.sendCloseMessage(WsSession.java:586)
    at org.apache.tomcat.websocket.WsSession.doClose(WsSession.java:488)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.onError(WsHttpUpgradeHandler.java:150)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.access$300(WsHttpUpgradeHandler.java:48)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onError(WsHttpUpgradeHandler.java:211)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:194)
    at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:198)
    at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:647)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)

编辑2: 我发现仅连接到 websocket-Server 时不会出现警告。仅当通过

发送一些文本时才会出现
session.getBasicRemote().sendText("TestMessage");

编辑3: 设置一个测试项目来实现 WebSocketClient:
启动.java

package de.test.client;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.Session;

public class Startup implements ServletContextListener
{
    Session session = null;
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        System.out.println("stopped Session");
    }

    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        System.out.println("try to establish Connection");
            try {
                this.session = ContainerProvider.getWebSocketContainer().connectToServer(TestClient.class,
                        new URI("ws://127.0.0.1:8081/TestServer/wsEndMessageBroker"));

                session.getBasicRemote().sendText("Hello WebsocketServer");
            } catch (DeploymentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (URISyntaxException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
}

TestClient.java

package de.test.client;

import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;

@ClientEndpoint
public class TestClient {

        @OnOpen
        public void onOpen(final Session userSession)
        {
            System.out.println("Connection established");
        }

        @OnClose
        public void onClose(final Session userSession, final CloseReason reason)
        {
            System.out.println("close am Clientendpoint(MessageBroker-Service)");
        }

        @OnMessage
        public void onMessage(String jsonMessage)
        {
            System.out.println("message: " + jsonMessage);
        }
        @OnError
        public void onError(Throwable error)
        {
            error.printStackTrace();
        }
}

WebSocket服务器:

测试服务器.java

package de.test.server;

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;

@ServerEndpoint("/wsEndMessageBroker")
public class TestServer
{
    public TestServer()
    {
        System.out.println("TestServer started");
    }

    @OnOpen
    public void onOpen(Session session)
    {
        System.out.println("New Connection established SessionID: " + session.getId());
    }

    @OnMessage
    public void message(Session session, String jsonMessage)
    {
        System.out.println("new Message: " + jsonMessage);
    }
    @OnClose
    public void onClose(Session session)
    {
        System.out.println("Closed Connection: " + session.getId());
    }
    @OnError
    public void onError(Throwable error)
    {
        error.printStackTrace();
        System.out.println("Connection were closed unexpected");
    }
}

记录客户端

INFORMATION: Initializing ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:29 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFORMATION: Using a shared selector for servlet write/read
Feb 12, 2016 9:54:29 AM org.apache.coyote.AbstractProtocol init
INFORMATION: Initializing ProtocolHandler ["ajp-nio-8009"]
Feb 12, 2016 9:54:29 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFORMATION: Using a shared selector for servlet write/read
Feb 12, 2016 9:54:29 AM org.apache.catalina.startup.Catalina load
INFORMATION: Initialization processed in 787 ms
Feb 12, 2016 9:54:29 AM org.apache.catalina.core.StandardService startInternal
INFORMATION: Starting service Catalina
Feb 12, 2016 9:54:29 AM org.apache.catalina.core.StandardEngine startInternal
INFORMATION: Starting Servlet Engine: Apache Tomcat/8.0.32
Feb 12, 2016 9:54:30 AM org.apache.jasper.servlet.TldScanner scanJars
INFORMATION: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Feb 12, 2016 9:54:30 AM org.apache.jasper.servlet.TldScanner scanJars
INFORMATION: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Versuche Verbindung aufzubauen
Connection established
Feb 12, 2016 9:54:30 AM org.apache.coyote.AbstractProtocol start
INFORMATION: Starting ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:30 AM org.apache.coyote.AbstractProtocol start
INFORMATION: Starting ProtocolHandler ["ajp-nio-8009"]
Feb 12, 2016 9:54:30 AM org.apache.catalina.startup.Catalina start
INFORMATION: Server startup in 956 ms
Feb 12, 2016 9:54:34 AM org.apache.catalina.core.StandardServer await
INFORMATION: A valid shutdown command was received via the shutdown port. Stopping the Server instance.
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol pause
INFORMATION: Pausing ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol pause
INFORMATION: Pausing ProtocolHandler ["ajp-nio-8009"]
Feb 12, 2016 9:54:34 AM org.apache.catalina.core.StandardService stopInternal
INFORMATION: Stopping service Catalina
stopped Session
Feb 12, 2016 9:54:34 AM org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads
WARNUNG: The web application [TestClient] appears to have started a thread named [Thread-5] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 sun.misc.Unsafe.park(Native Method)
 java.util.concurrent.locks.LockSupport.park(Unknown Source)
 java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
 java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
 java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 java.lang.Thread.run(Unknown Source)
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol stop
INFORMATION: Stopping ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol stop
INFORMATION: Stopping ProtocolHandler ["ajp-nio-8009"]
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol destroy
INFORMATION: Destroying ProtocolHandler ["http-nio-8080"]
Feb 12, 2016 9:54:34 AM org.apache.coyote.AbstractProtocol destroy
INFORMATION: Destroying ProtocolHandler ["ajp-nio-8009"]

日志服务器端

Feb 12, 2016 9:54:26 AM org.apache.coyote.AbstractProtocol init
INFORMATION: Initializing ProtocolHandler ["http-nio-8081"]
Feb 12, 2016 9:54:26 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFORMATION: Using a shared selector for servlet write/read
Feb 12, 2016 9:54:26 AM org.apache.coyote.AbstractProtocol init
INFORMATION: Initializing ProtocolHandler ["ajp-nio-8010"]
Feb 12, 2016 9:54:26 AM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFORMATION: Using a shared selector for servlet write/read
Feb 12, 2016 9:54:26 AM org.apache.catalina.startup.Catalina load
INFORMATION: Initialization processed in 992 ms
Feb 12, 2016 9:54:26 AM org.apache.catalina.core.StandardService startInternal
INFORMATION: Starting service Catalina
Feb 12, 2016 9:54:26 AM org.apache.catalina.core.StandardEngine startInternal
INFORMATION: Starting Servlet Engine: Apache Tomcat/8.0.32
Feb 12, 2016 9:54:27 AM org.apache.jasper.servlet.TldScanner scanJars
INFORMATION: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Feb 12, 2016 9:54:27 AM org.apache.jasper.servlet.TldScanner scanJars
INFORMATION: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Feb 12, 2016 9:54:27 AM org.apache.coyote.AbstractProtocol start
INFORMATION: Starting ProtocolHandler ["http-nio-8081"]
Feb 12, 2016 9:54:27 AM org.apache.coyote.AbstractProtocol start
INFORMATION: Starting ProtocolHandler ["ajp-nio-8010"]
Feb 12, 2016 9:54:27 AM org.apache.catalina.startup.Catalina start
INFORMATION: Server startup in 895 ms
TestServer started
New Connection established SessionID: 0
new Message: Hello WebsocketServer
java.io.IOException: Unable to write the complete message as the WebSocket connection has been closed
    at org.apache.tomcat.websocket.WsSession.registerFuture(WsSession.java:664)
    at org.apache.tomcat.websocket.FutureToSendHandler.get(FutureToSendHandler.java:92)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessageBlock(WsRemoteEndpointImplBase.java:277)
    at org.apache.tomcat.websocket.WsSession.sendCloseMessage(WsSession.java:586)
    at org.apache.tomcat.websocket.WsSession.doClose(WsSession.java:488)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.onError(WsHttpUpgradeHandler.java:150)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.access$300(WsHttpUpgradeHandler.java:48)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onError(WsHttpUpgradeHandler.java:211)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:194)
    at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:198)
    at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:647)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)
Connection were closed unexpected
Closed Connection: 0
java.io.IOException: Eine vorhandene Verbindung wurde vom Remotehost geschlossen
    at sun.nio.ch.SocketDispatcher.read0(Native Method)
    at sun.nio.ch.SocketDispatcher.read(Unknown Source)
    at sun.nio.ch.IOUtil.readIntoNativeBuffer(Unknown Source)
    at sun.nio.ch.IOUtil.read(Unknown Source)
    at sun.nio.ch.SocketChannelImpl.read(Unknown Source)
    at org.apache.tomcat.util.net.NioChannel.read(NioChannel.java:137)
    at org.apache.coyote.http11.upgrade.NioServletInputStream.fillReadBuffer(NioServletInputStream.java:136)
    at org.apache.coyote.http11.upgrade.NioServletInputStream.doRead(NioServletInputStream.java:80)
    at org.apache.coyote.http11.upgrade.AbstractServletInputStream.read(AbstractServletInputStream.java:124)
    at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:60)
    at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:186)
    at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:198)
    at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:647)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)
Connection were closed unexpected

最佳答案

堆栈跟踪显示,有一个名为“Thread-5”的线程在应用程序关闭时尚未停止。这可能只是被视为警告,因为 tomcat 能够 handle such problems 。但是,当您每次重新部署应用程序后没有重新启动 Tomcat 时,这也可能是内存泄漏的原因。

您可以在 java.lang.Thread 类中设置条件断点,以便找出名称为“Thread-5”的线程的创建位置。 找到线程的创建者后,您可以研究如何正常关闭创建该线程的组件。

关闭功能可以放置在 ServletContextListener 的 contextDestroyed() 方法中或带有 Spring @PreDestroy 注释的方法中。

关于java - Tomcat 8停止Tomcat时出现内存泄漏警告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35348802/

相关文章:

java - 获取当前测试用例在 Junit 中运行的类

java - 计算对象执行的功能数量

apache - AJP 不适用于服务器别名

java - 用eclipse开普勒运行tomcat

javascript - 您可以在不接触服务器的情况下通过网络套接字将客户端连接到客户端吗?

java - 简单Java程序中的循环

java - 配置 hibernate 4.x 以使用带有 Redis 的 Elasticache 作为二级缓存

java - Apache Tomcat 异常 - 打开的文件太多

python - 在python websocket客户端中,为什么ping_interval应该大于ping_timeout

python - 如何通过 websockets 发送 pygame 图像?