【问题标题】:Java Threads: Memory LeakJava 线程:内存泄漏
【发布时间】:2012-02-17 01:01:03
【问题描述】:

我用 Java 实现了简单的服务器-客户端聊天。这里是服务器的来源:

public class Server {
    final private static int PORT = 50000;

    private static class Read extends Thread {
        private static Socket socket;
        private static String address;

        public Read(Socket socket) {
            this.socket = socket;
            address = socket.getInetAddress().toString().substring(1);
        }

        public void run() {
            try {

                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String msg;

                while (true) {
                    msg = in.readLine();
                    if (msg == null) {
                        in.close();
                        return;
                    }

                    System.out.println(address + ": " + msg);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    private static class Write extends Thread {
        private static Socket socket;

        public Write(Socket socket) {
            this.socket = socket;
        }

        public void run() {
            try {

                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
                String msg;

                while (true) {
                    if (socket.isClosed()) {
                        out.close();
                        return;
                    }
                    if (stdin.ready()) {
                        msg = stdin.readLine();
                        out.println(msg);
                    }
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket;
        boolean listening = true;

        serverSocket = new ServerSocket(PORT);

        while (listening) {
            Socket socket = serverSocket.accept();
            String address = socket.getInetAddress().toString().substring(1);
            System.out.println("Connection Established " + address);

            Thread read = new Read(socket);     
            Thread write = new Write(socket);

            read.start();
            write.start();

            try {
                read.join();
                write.join();
            } catch(InterruptedException e) {

            }   

            socket.close();
            System.out.println("Connection Closed " + address);
        }
        serverSocket.close();
    }
}

它工作正常,但有一个问题。对于每个已建立的连接,内存都会不断增长。我认为问题是为线程分配的内存之后没有释放,但我不太确定。我该如何解决?

编辑:客户端程序:

class Client {
    final private static int PORT = 50000;

    private static class Read extends Thread {
        private Socket socket;
        private String address;

        public Read(Socket socket) {
            this.socket = socket;
            address = socket.getInetAddress().toString().substring(1);
        }

        public void run() {
            try {

                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String msg;

                while (true) {
                    msg = in.readLine();
                    if (msg == null) {
                        System.out.println("Connection closed " + address);
                        System.exit(0);
                    }
                    System.out.println(address + ": " + msg);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    private static class Write extends Thread {
        private Socket socket;

        public Write(Socket socket) {
            this.socket = socket;
        }

        public void run() {
            try {

                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                Scanner sc = new Scanner(System.in);
                String msg;

                while (true) {
                    msg = sc.nextLine();
                    out.println(msg);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) throws IOException {
        PrintWriter out;
        BufferedReader in;
        Scanner sc = new Scanner(System.in);
        while (true) {  //for the test only
            Socket socket = null;
        try {   
            socket = new Socket("78.90.68.125", PORT);
        } catch(java.net.ConnectException e) {
            System.out.println("Connection error: host unreachable");
            System.exit(1);
        }
/*
        String address = socket.getInetAddress().toString().substring(1);
        System.out.println("Connection established " + address);
        Thread read = new Read(socket);     
        Thread write = new Write(socket);

        read.start();
        write.start();

        try {
            read.join();
            write.join();
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
        finally {
*/      
        socket.close();
//      }
        //System.out.println("Connection closed " + address);
        }
    }
}

【问题讨论】:

    标签: java multithreading sockets


    【解决方案1】:

    尝试制作

    private static class Read extends Thread {
     private static Socket socket;
     private static String address;
    

    private static class Write extends Thread {
     private static Socket socket;
    

    到非静态。

    另外,我不知道你是如何检查内存的,但请记住 Java 是垃圾收集的,你会看到最初的内存使用量增加,直到垃圾收集器 (GC) 收集它,并且会再次增加,直到下一次 GC 运行。所以它会持续增加很长一段时间没有任何下降,然后才会出现内存泄漏,否则你很高兴。


    我按原样运行上述代码并运行了大约 1-2 小时,在使用 JDK 6 的 Mac 机器上稳定的内存使用量约为 54MB。我没有使用 jdk 附带的JConsole 来查看内存使用情况。我没有发现任何问题。

    下面是我在回答中提到的图表,你有高峰和低谷..最后当我停止客户端时它是平坦的。

    【讨论】:

    • 我使用系统监视器来检查内存。程序达到 70mb,然后客户端抛出异常:Exception in thread "main" java.net.NoRouteHostException: Cannot assign requested address。将字段更改为非静态无效。
    • 你的客户是什么?发生故障时您有多少连接?根据您的逻辑,任何给定时间似乎只建立了一个连接?
    • 为了测试服务器我修改了客户端看起来像while (true) { Socket socket = new Socket("IP", PORT); .... socket.close(); }
    • 您似乎没有内存泄漏,但达到了最大端口号限制
    • 我不太确定我理解了。
    【解决方案2】:

    伊万,

    使用线程的一些事情。

    永远不要这样做:

     try {
       read.join();
       write.join();
     } catch(InterruptedException e) {
     }   
    

    总是在 catch 子句中加入一些东西,比如 log.error。你没有机会知道它发生了。

    然后,所有流/关闭等必须进入 finally 块。否则你永远无法确保关闭所有必要的东西。

    您可能希望重用连接。尝试这个: http://commons.apache.org/pool/

    您能否告诉我们您是否经常访问 sysout 以关闭连接? 基本上每次打开连接和关闭连接时都尝试创建日志语句。可能你看到了你缺少的东西。

    【讨论】:

      【解决方案3】:

      尝试将您的 socket.close() 放入 finally 块中以确保它运行。

      但我认为您的代码可能存在更大的问题,因为您没有使用连接池,因此您不必要地打开新连接。

      【讨论】:

        猜你喜欢
        • 2016-12-09
        • 2014-10-29
        • 2013-12-18
        • 2015-05-20
        • 2017-02-04
        • 2020-08-08
        • 2015-11-17
        • 2020-12-08
        • 2018-06-19
        相关资源
        最近更新 更多