Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/376.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Java总是消耗更多的内存?_Java_Memory - Fatal编程技术网

为什么Java总是消耗更多的内存?

为什么Java总是消耗更多的内存?,java,memory,Java,Memory,所以我有一个小的客户端代码 public class Client { private static Socket socket; private static ObjectOutputStream out; public static void main(String[] args) { while (true) { try { if (socket != null) {

所以我有一个小的客户端代码

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

}
让我头疼的是,当我用javaw.exe运行代码时,我发现java每2-3秒就要消耗大约10kb的内存。所以内存使用量一直在增长

java真的有那么糟糕吗?还是有其他问题


我在while循环中运行了一段时间,内存使用量增加了1000 kb。 java gargabe在使用“tmp”变量后不收集它吗

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

当一个变量实际超出范围时,垃圾收集器永远不会运行,或者你会把大部分时间花在GC代码上

取而代之的是(这是一个相当简单的过程),它会等到您使用的内存达到阈值,然后才开始释放内存


这就是你所看到的,你的内存消耗增长如此之慢,以至于需要很长时间才能达到下一个阈值并真正释放内存。

当一个变量实际超出范围时,垃圾收集器永远不会运行,或者你会把大部分时间花在GC代码上

取而代之的是(这是一个相当简单的过程),它会等到您使用的内存达到阈值,然后才开始释放内存


这就是您所看到的,您的内存消耗增长非常缓慢,需要很长时间才能达到下一个阈值并实际释放内存。

IO由套接字实现缓存,直到刷新为止。因此,要么您真的从套接字读取输入/输出(或者对流调用#flush()),要么您关闭套接字。

IO由套接字实现缓存,直到刷新为止。因此,要么您真的从套接字读取输入/输出(或者对流调用#flush()),要么您关闭套接字。

看起来您从未关闭套接字或刷新ObjectOutputStream。还请注意,Java垃圾收集基本上不是在您希望的时候发生,而是在垃圾收集器认为合适的时候发生。

看起来您从未关闭套接字或刷新ObjectOutputStream。还要注意的是,Java垃圾收集基本上不是在您希望的时候发生的,而是在垃圾收集器认为合适的时候发生的。

我不认为添加close是您的问题,因为我认为您试图做的是继续写入流。您是否尝试过退出.flush()。这将刷新内容,使其不再位于内存中

我不认为添加close是你的问题,因为我认为你试图做的是继续向流中写入内容。您是否尝试过退出.flush()。这将刷新内容,使其不再位于内存中

对我来说,逻辑本身就是罪魁祸首,不存在退出while循环的条件。
同样,没有刷新。

对我来说,逻辑本身就是罪魁祸首,没有条件退出while循环。
同样没有刷新。

因此,我已经为您的客户机编写了一个简单的测试服务器,现在我正在运行这两个服务器,并且内存使用似乎没有增加

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

}
你有什么不同

因此,我更详细地研究了这个程序的内存使用情况,并发现了一个有用的工具,即隐藏在我的系统开发菜单中的“Java监视和管理控制台”:

下面是运行客户端程序时内存使用情况的屏幕截图(记住,我每100毫秒发送一个对象)

我们可以看到内存使用率有一条锯齿曲线——它是线性增长的,然后是垃圾收集,然后下降到基本使用率。在一段初始时间之后,VM会更频繁地(从而更快地)执行GC。现在,没问题


这是一个变体程序,我并不总是发送相同的字符串,但每次都发送不同的字符串:

private static void runClient() {
    int i = 0;
    while (true) {
        try {
            i++;
            if (socket != null) {
                out.writeObject("Hello " + i + " ...");
                Thread.sleep(100);
                System.out.print(",");
(其余如上图所示)。我认为这需要更多的内存,因为ObjectOutputStream必须记住哪些对象已经被发送,以便在它们再次出现时能够重用它们的标识符

但不,它看起来很相似:

39和40之间的小不规则是由这里的“执行GC”按钮进行的手动完全GC-尽管没有太大变化


我让最后一个程序运行更长一点,现在我们看到ObjectOutputStream仍然保存着对字符串的引用

在半小时内,我们的程序消耗了大约2MB的内存(在64位虚拟机上)。这一次,它发送了18000个字符串。因此,每个字符串平均使用大约100字节的内存

每根弦的长度都在11到17个字符之间。由于StringBuilder的分配策略,后者(大约一半)实际上使用32个字符数组,前者使用16个字符数组。这些需要64或32字节+阵列开销(至少多12个字节,更有可能更多)。此外,字符串对象本身需要一些内存开销(对于类和我记得的字段,至少8+8+4+4+4=28,更可能是更多),因此每个字符串平均(至少)88字节。此外,ObjectOutputStream中可能存在一些开销,以在某些数据结构中维护这些对象

因此,损失并不比实际需要的多

啊,如果您不打算再次发送任何对象,如何避免ObjectOutputStream(以及相应的ObjectInputStream)存储这些对象的一个技巧是:每隔大约一千个字符串调用它的
重置
方法。

这是我在一个多小时后终止程序前的最后一个屏幕截图:

为了进行比较,我添加了名为reset,让程序再运行两个小时(还有一点):


它仍然像以前一样收集内存,但现在当我单击“performgc”时,它会清除所有内容并返回到以前的状态(略高于1MB)。(在堆的末尾也会这样做,但我不想等这么久。)

因此,我为您的客户机编写了一个简单的测试服务器,我很高兴