【问题标题】:Memory leak in a multithreaded Java web server多线程 Java Web 服务器中的内存泄漏
【发布时间】:2018-07-14 11:23:00
【问题描述】:

我有一个多线程 Java Web 服务器,它接受来自客户端的请求。

RAM中的每个请求都会为这个请求分配内存,但在socket关闭后不会被清除。仅在重新启动 Web 服务器后才会进行清理。

如果是内存泄漏,那么会发生在代码的哪一部分呢?

我的代码:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Main {
    public static void main(String[] args) {
        try (ServerSocket server = new ServerSocket(80)) {
            Socket socket = server.accept();
            while (true) {
                new Thread(new Client(socket)).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Client implements Runnable {
    private Socket socket;

    Client(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            System.out.println("run");
        } finally {
            try {
                socket.close();
                System.out.println("Socket closed: " + socket);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

【问题讨论】:

  • 使用来自 OutOfMemoyError 的堆栈跟踪和 Java 版本的详细信息以及您正在运行的平台更新问题

标签: java multithreading sockets memory-leaks


【解决方案1】:

while(true) 循环中创建线程绝不是一个好主意。
默认情况下,每个创建的 Thread 都会分配; 1mb 用于 64 位 jvm,128 kb 用于 32 位 jvm 的堆栈(这是用于 linux 操作系统,其他详细信息 here)。尝试ExecutorService 处理这类事情。线程是宝贵的资源。

【讨论】:

    【解决方案2】:

    为每个传入的连接启动一个新线程是非常低效的,并且可能是内存泄漏的原因。

    正确的做法是使用一个线程池,其中有固定数量的可重复使用的线程。

    还值得指出的是,线程消耗大量的本地内存(Java 堆外)而堆内存非常少,因此不会触发垃圾回收,而垃圾回收又会调用终结器来释放本地端。

    编辑: 此外,代码并没有按照您的想法执行。将 accept 置于循环之外意味着代码将一遍又一遍地为同一个连接启动线程,而不是为每个连接创建一个新线程。

    【讨论】:

    • 虽然为每个传入连接启动线程效率极低,但这并不是内存泄漏。我必须检查一下——我刚刚创建并启动了一百万个线程,但只使用大小有限的集合引用了其中的 100 个,并且没有任何内存不足的问题。 (不过笔记本电脑确实很快变热了)已完成的线程会被垃圾收集器清理,并且不会自行泄漏内存。
    • 只有当其他东西在驱动 GC 时,提供的代码分配只分配套接字(来自接受)和新线程,如果没有其他东西正在分配对象,那么不会触发调用终结器所需的 GC in 以释放线程的本机组件。没有看到实际的 OOM 堆栈跟踪很难说,但我 90% 确定这将是本机内存耗尽。我假设您在 64 位机器上进行了测试。
    • LimitedLinkedHashMap<Thread, Boolean> threads = new LimitedLinkedHashMap<>(100); while (true) { Thread t = new Thread(); threads.put(t, Boolean.TRUE); t.start(); } - 在 64 位机器上是,但没有 Xmx 设置;总内存使用量保持在 245Mb 不变。唯一驱动 GC 的是线程分配——没有其他东西会占用内存。
    • 谢谢。接受在循环内,我在创建问题时弄错了:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-09
    • 2012-02-17
    • 1970-01-01
    • 2014-08-02
    • 2016-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多