开发者

Why is Java constantly eating more memory?

开发者 https://www.devze.com 2023-02-13 06:59 出处:网络
So i have this small client side code public class Client { private static Socket socket; private static ObjectOutputStream out;

So i have this small client side code

public class Client {

    private static Socket socket;
    private static ObjectOutputStream out;

    public static void main(String[] args) {
        while (true) {
            try {
                if (socket != null) {
                    out.writeObject("Hello...");
                    Thread.sleep(1500);
                } else {
                    socket = new Socket("myhost", 1234);
                    out = new ObjectOutputStream(socket.getOutputStream());
                    System.out.println("connected to server");
                }
            } catch (final Exception e) {
                      //set socket to null for reconnecting
            }
        }
    }

}

What bugs me is that when i run the code with javaw.exe, i see that the java is eating ~10kb more memory every 2-3 sec. So memory usage keeps growing and growing...

Is java really that bad or is there something else wrong?


I ran this code in while loop for a while and memory usage increased for 1000 kb. Doesn't java gargabe collect the 'tmp' variable after it's used?

try {
    if (socket == null) {
        final Socket tmp = new Socket(开发者_JAVA百科"localhost", 1234);
        if (tmp != null) {
           socket = tmp;
        }
        Thread.sleep(100);
    }
} catch (final Exception e) {       
}


So, I've written a simple test server for your client and I'm now running both, and there seems to be no increase in memory usage.

import java.net.*;
import java.io.*;


/**
 * example class adapted from 
 * http://stackoverflow.com/questions/5122569/why-is-java-constantly-eating-more-memory
 */
public class Client {

    private static Socket socket;
    private static ObjectOutputStream out;

    private static void runClient() {
        while (true) {
            try {
                if (socket != null) {
                    out.writeObject("Hello...");
                    Thread.sleep(100);
                    System.out.print(",");
                } else {
                    socket = new Socket("localhost", 1234);
                    out = new ObjectOutputStream(socket.getOutputStream());
                    System.out.println("connected to server");
                }
            } catch (final Exception e) {
                      //set socket to null for reconnecting
                e.printStackTrace();
                return;
            }
        }
    }

    private static void runServer() throws IOException{
        ServerSocket ss = new ServerSocket(1234);
        Socket s = ss.accept();
        InputStream in = s.getInputStream();
        byte[] buffer = new byte[500];
        while(in.read(buffer) > 0) {
            System.out.print(".");
        }
    }


    public static void main(String[] args) 
        throws IOException
    {
        if(args.length > 0) {
            runServer();
        }
        else {
            runClient();
        }
    }

}

What are you doing different?

So, I've looked a bit more detailed at the memory usage of this program, and found for this a useful tool, the "Java Monitoring and Management console" hidden in the development menu of my system :-)

Here is a screenshot of the memory usage while running the client program some time (each 100 ms I send an object, remember) ...

Why is Java constantly eating more memory?

We can see that the memory usage has a saw tooth curve - it is lineary increasing, then comes a garbage collection and it is falling down to the base usage. After some initial period the VM is doing the GC more often (and thus more quickly). For now, no problem.


Here is a variant program where I did not send the same string always, but a different one each time:

private static void runClient() {
    int i = 0;
    while (true) {
        try {
            i++;
            if (socket != null) {
                out.writeObject("Hello " + i + " ...");
                Thread.sleep(100);
                System.out.print(",");

(The rest is like above). I thought this would need more memory, since the ObjectOutputStream has to remember which Objects are already sent, to be able to reuse their identifiers in case they come again.

But no, it looks quite similar:

Why is Java constantly eating more memory?

The little irregularity between 39 and 40 is a manual full GC made by the "Perform GC" button here - it did not change much, though.


I let the last program run a bit longer, and now we see that the ObjectOutputStream still is holding references to our Strings ...

Why is Java constantly eating more memory?

In half an hour our program ate about 2 MB of memory (on a 64-bit-VM). In this time, it sent 18000 Strings. So, each of the Strings used in average about 100 bytes of memory.

Each of those Strings was between 11 and 17 chars long. the latter ones (about the half) are using in fact 32-char-arrays, the former ones 16-char-arrays, because of the allocation strategy of the StringBuilder. These take 64 or 32 bytes + array-overhead (at least 12 more bytes, more likely more). Additionally the String objects themselves take some memory overhead (at least 8+8+4+4+4 = 28 for class and the fields I remember, more likely more), so we have in average (at least) 88 bytes per String. In addition there likely is some overhead in the ObjectOutputStream to maintain these objects in some data structure.

So, not much more lost than in fact needed.

Ah, one tip of how to avoid the ObjectOutputStream (and the corresponding ObjectInputStream, too) storing the objects, if you don't plan on sending any of them again: Invoke its reset method every some thousand strings or so.

Here is a last screenshot before I kill the program, after a bit more than an hour:

Why is Java constantly eating more memory?

For comparison, I added the named reset and let the program run two more hours (and a bit):

Why is Java constantly eating more memory?

It still collects memory as before, but now when I click on "Perform GC" it cleans everything and goes back at the state before (just a bit over 1 MB). (It would do the same when coming at the end of Heap, but I didn't want to wait this long.)


Well the garbage collector never runs when a variable actually goes out of scope, or you'd spend most of your time in the GC code.

What it does instead (and this is quite a simplification) is it waits until your memory used reaches a threshold, and only then does it start releasing memory.

This is what you're seeing, your memory consumption is increasing so slowly that it'll take a long time to reach the next threshold and actually free memory.


I don't think adding close is your problem because from what i think you are trying to do is keep writing to the stream. Have you tried out.flush(). This flushes the content so that its not in memory anymore.


It looks like you never close the Socket or flush the ObjectOutputStream. Also note that Java garbage collection basically happens not when you want it to but when the garbage collector sees fit.


IO is cached by the Socket implementation until it is flushed. So either you really read the input / output from the socket (or call #flush() on your streams) or you close the socket.


To me logic itself is culPrit, there is no condition to come out of the while loop. Again no flush.


ObjectOutputStream cached every object you send, in case you send it again. To clear this you need to call the reset() method

Reset will disregard the state of any objects already written to the stream. The state is reset to be the same as a new ObjectOutputStream. The current point in the stream is marked as reset so the corresponding ObjectInputStream will be reset at the same point. Objects previously written to the stream will not be refered to as already being in the stream. They will be written to the stream again.

BTW: 10 KB is worth about 0.1 cents of memory. One minute of your time at minimum wage is worth 100x times this. I suggest you consider what is the best use of your time.

0

精彩评论

暂无评论...
验证码 换一张
取 消