首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用Jetty 9 ShutdownThread避免内存泄漏

如何使用Jetty 9 ShutdownThread避免内存泄漏
EN

Stack Overflow用户
提问于 2014-10-30 18:12:52
回答 1查看 2.2K关注 0票数 0

我们有一个使用Jetty WebSocketClient连接到服务器的工具。有时我们需要重新启动到服务器的连接。使用当前的代码,我们会在org.eclipse.jetty.util.thread.ShutdownThread上遇到内存泄漏,在那里保存所有使用过的org.eclipse.jetty.websocket.client.WebSocketClient实例。

http://www.eclipse.org/jetty/documentation/current/jetty-websocket-client-api.html的文档一样,唯一要做的事情就是调用client.stop()

代码或Jetty本身有什么问题吗?

下面是代码的“最小”示例。在其中,主运行一个“测试”,跟踪和比较所消耗的内存。

结果是:more heap used: 97 / 100 -告诉我有什么不对劲.

Task处理到服务器的连接(并保留其他信息):

代码语言:javascript
复制
package example;

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

import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;

class Task {

    public enum StopReason {

        CLOSED,

        EXIT,

        RESTART
    }

    private WebSocketClient client;

    private ClientSocket socket;

    private URI uri;

    public Task(URI uri) {
        this.uri = uri;
    }

    public void restart() throws IOException {
        System.out.println("Task.restart");
        stop(StopReason.RESTART);

        synchronized (this) {
            try {
                this.wait(10);
            } catch (InterruptedException e) {
                // ignore
            }
        }

        start();
    }

    public void start() throws IOException {
        System.out.println("Task.start");
        client = new WebSocketClient();
        client.getPolicy().setIdleTimeout(120000);
        client.setMaxIdleTimeout(1000);

        socket = new ClientSocket(this);

        try {
            client.start();
            System.out.println("Task started");
        } catch (Exception e) {
            throw new IOException("Failed to start WebSocketClient: " + e.getLocalizedMessage(), e);
        }

        ClientUpgradeRequest request = new ClientUpgradeRequest();
        request.setRequestURI(uri);
        client.connect(socket, request.getRequestURI(), request);
        System.out.println("Task connected");
    }

    public void stop(Task.StopReason stopReason) throws IOException {
        System.out.println("Task.stop " + stopReason);

        if (stopReason != StopReason.CLOSED) {
            try {
                client.stop();
            } catch (Exception e) {
                throw new IOException("Task Error at 'stop': " + e.getLocalizedMessage(), e);
            }

            client.destroy();

            try {
                client.getConnectionManager().stop();
            } catch (Exception e) {
                System.err.println("Task Error at 'stop': " + e.getLocalizedMessage());
            }

            client.getConnectionManager().destroy();
        }

        System.out.println("Task.stop done " + stopReason);
    }
}

主类运行“测试”:

代码语言:javascript
复制
package example;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Map;

import javax.websocket.DeploymentException;
import javax.websocket.server.ServerEndpoint;

import org.glassfish.tyrus.server.Server;

import example.Task.StopReason;

@SuppressWarnings("javadoc")
@ServerEndpoint(value = "/test")
public class MemoryLeakDebug {

    public static void main(String[] args) throws IOException, URISyntaxException, DeploymentException {
        Map<String, Object> config = Collections.emptyMap();
        Server server = new Server("localhost", 10000, "/test", config, DummyEndpoint.class);
        server.start();

        Task task = new Task(new URI("ws://localhost:10000/test/test"));
        task.start();

        long[] useds = new long[100];

        try {
            for (int i = 0; i < useds.length; i++) {
                System.out.println("------------------------");
                Runtime.getRuntime().gc();
                long used = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
                System.out.println("used: " + used);
                useds[i] = used;

                task.restart();

                synchronized (task) {
                    try {
                        task.wait(100);
                    } catch (InterruptedException e) {
                        System.err.println(e.getLocalizedMessage());
                    }
                }
            }
        } catch (AssertionError e) {
            throw e;
        } finally {
            task.stop(StopReason.EXIT);
            server.stop();

            StringBuilder sb = new StringBuilder("useds:\n");
            int index = 0;
            int count = 0;
            int moreCount = 0;
            long last = -1;

            for (long used : useds) {
                sb.append((index++) + "\t" + used + "\n");

                if (used > 0) {
                    count++;
                }

                if (last != -1) {
                    if (used > last) {
                        moreCount++;
                    }
                }

                last = used;
            }

            System.out.println(sb.toString());
            System.out.println(moreCount + " / " + count);

            if (moreCount > count * .75) {
                throw new IllegalStateException("more heap used: " + moreCount + " / " + count);
            }
        }
    }
}

模拟端点:

代码语言:javascript
复制
package example;

import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig;
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;

@SuppressWarnings("javadoc")
@ServerEndpoint(value = "/test")
public class DummyEndpoint {

    public DummyEndpoint() {
        System.out.println("DummyEndpoint");
    }

    @OnClose
    public void handleOnClose(Session session, CloseReason reason) {
        System.out.println("DummyEndpoint@OnClose: " + reason + " " + session);
    }

    @OnError
    public void handleOnError(Session session, Throwable error) {
        System.out.println("DummyEndpoint@OnError: " + error + " " + session + " " + error.toString());
    }

    @OnMessage
    public void handleOnMessage(Session session, String message) {
        System.out.println("DummyEndpoint@OnMessage: " + message + " " + session);
    }

    @OnOpen
    public void handleOnOpen(final Session session, EndpointConfig conf) {
        System.out.println("DummyEndpoint@OnOpen: " + conf + " " + session);
    }
}

简化的虚拟客户端:

代码语言:javascript
复制
package example;

import java.io.IOException;

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;

import example.Task.StopReason;

@SuppressWarnings("javadoc")
@WebSocket
public class ClientSocket {

    private Session session;
    private Task task;

    public ClientSocket(Task task) {
        this.task = task;
    }

    @OnWebSocketClose
    public void handleClose(int statusCode, String reason) {
        System.out.println("ClientSocket@OnWebSocketClose " + statusCode + " " + reason);
        session = null;

        try {
            task.stop(StopReason.CLOSED);
        } catch (IOException e) {
            throw new IllegalStateException("Error at 'handleClose': " + e.getLocalizedMessage(), e);
        }
    }

    @OnWebSocketConnect
    public void handleConnect(Session session) {
        System.out.println("ClientSocket@OnWebSocketConnect " + session);
        this.session = session;
    }

    @OnWebSocketError
    public void handleError(Throwable cause) {
        System.err.println("ClientSocket@OnError" + cause);
        cause.printStackTrace();
    }

    @OnWebSocketMessage
    public void handleMessage(String message) {
        System.out.println("ClientSocket@OnWebSocketMessage " + message);
    }

    public boolean isConnected() {
        return session != null;
    }
}

我们的依赖关系:

代码语言:javascript
复制
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>example</groupId>
    <artifactId>WebSocketClientDebug</artifactId>
    <version>0.1.0-SNAPSHOT</version>

    <properties>
        <tyrus-version>[1.8.3,1.9)</tyrus-version>
    </properties>

    <dependencies>
        <!-- + + + + + external dependencies + + + + + -->
        <dependency>
            <groupId>org.eclipse.jetty.websocket</groupId>
            <artifactId>websocket-client</artifactId>
            <version>9.2.3.v20140905</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>[2.6.0,3.2.0)</version>
        </dependency>

        <!-- + + + + + external test dependencies + + + + + -->
        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-client</artifactId>
            <version>[1.8.3,1.9)</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-container-grizzly-server</artifactId>
            <version>${tyrus-version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-server</artifactId>
            <version>${tyrus-version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.tyrus</groupId>
            <artifactId>tyrus-container-grizzly-client</artifactId>
            <version>${tyrus-version}</version>
        </dependency>
    </dependencies>
</project>
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-10-30 18:19:51

这是Jetty的WebSocketClient行为中的一个bug。

请参阅bug.cgi?id=444748

此修补程序适用于Jetty9.2.4(目前正在进行阶段测试,并将在今后7天内发布)

如果您想访问这个分阶段版本,请在irc.freenode.net/#jetty上与我联系,或者等待它发布。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26660366

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档