简体   繁体   中英

Java Threads: Memory Leak

I implemented simple server-client chat in Java. Here the source for the server:

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();
    }
}

It works fine but there is a problem. For every established connection the memory continuously grows. I presume the problem is that the memory allocated for the threads is not released afterwards but I'm not quite sure. How can I fix that?

EDIT: The client program:

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);
        }
    }
}

Ivan,

a few things to work with Threading.

Never do this:

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

Always put something into the catch clause, and be it a log.error. You have no chance to know it occurs.

Then, all streams/closings etc must go into a finally block. Otherwise you cannever be sure to close everything necessary.

YOu might want to reuse connections. Try this: http://commons.apache.org/pool/

Can you tell us if you reach the sysout for closing connections regulary? Basically try to create log statements every time you open a connection and every time you close it. Probably you see what you are missing.

Try making

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

and

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

to non-static.

Also, I dont know how you checking for memory, but do remember that Java is garbage collected and you will see increase in memory usage initially till the time garbage collector (GC) collects it and will increase again till next GC run. So it consistently increasing without any dip for long time only then there is a memory leak else you are good to go.


I ran the above code as is and ran for around 1-2 hours and it is steady memory usage of around 54MB on Mac machine using JDK 6. I am not using JConsole that comes with jdk to see mem usage. I found NO issues.

Below is the graph as I mentioned in my ans also, you have peak and dip ..in the end when I stopped client it is flat. 在此输入图像描述

Try putting your socket.close() inside a finally block to ensure that it runs.

But I think your code may have bigger problems in that since you are not using a connection pool, you are needlessly opening new connections.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM