【问题标题】:Simple Class - Is it a Memory Leak?简单类 - 是内存泄漏吗?
【发布时间】:2012-06-12 10:19:37
【问题描述】:

我有一个非常简单的类,它有一个整数变量。我只是将变量“i”的值打印到屏幕上并增加它,并使线程休眠 1 秒。当我针对这种方法运行分析器时,即使我没有创建任何新变量,内存使用量也会缓慢增加。执行此代码大约 16 小时后,我看到内存使用量增加到 4 MB(启动程序时最初为 1 MB)。我是Java新手。任何人都可以帮助解释我哪里出错了,或者为什么即使没有创建新变量,内存使用量也会逐渐增加?提前致谢。

我正在使用 netbeans 7.1 及其分析器来查看内存使用情况。

    public static void main(String[] args)
    {
        try
        {
            int i = 1;
            while(true)
            {
                System.out.println(i);
                i++;
                Thread.sleep(1000);
            }
        }
        catch(InterruptedException ex)
        {
            System.out.print(ex.toString());
        }
    }

程序启动时的初始内存使用量:1569852 字节。

执行循环 16 小时后的内存使用量:4095829 字节

【问题讨论】:

  • 为什么不使用“真正的”分析器或进行堆转储来检查消耗所有内存的内容?我打赌很多 char 数组。

标签: java memory-leaks


【解决方案1】:

没有 printlns 会发生这种情况吗?换句话说,可能是让 printlns 显示在控制台上是消耗内存的原因。

【讨论】:

  • 是的,如果没有将变量值打印到屏幕上,确实会发生这种情况。
  • 如果是 System.out.println() 方法,则在调用 System.out.println 后尝试调用垃圾收集器(“System.gc();”)
  • -1:不是答案,而是关于问题的问题。这应该是一条评论。
  • @Don Roby 不,这是对导致问题的原因的建议和解决方案,因为他的回答可能是在没有 printlns 的情况下发生。愚蠢的反对票。
【解决方案2】:

不一定是内存泄漏。当 GC 运行时,将收集在 System.out.println(i); 语句中分配的(我假设)对象。 Java 中的内存泄漏是指内存被 GC无法回收的无用对象填满。

println(i) 使用Integer.toString(int)int 转换为字符串,即每次分配一个新的String。这不是泄漏,因为一旦将字符串复制到输出缓冲区,该字符串将变得无法访问并成为 GC 的候选对象。

其他可能的内存分配来源:

  • Thread.sleep 可能会在后台分配对象。

  • 某些私有 JVM 线程可能导致此问题。

  • 分析器用于监视 JVM 状态的“java 代理”代码可能会导致此问题。它必须组装数据并将数据通过套接字发送到分析器应用程序,这很可能涉及分配 Java 对象。它也可能在 JVM 的堆或非堆内存中积累东西。

但只要在 GC 运行时 可以 回收空间,这并不重要。如果不能,那么您可能在您正在使用的分析器中发现了 JVM 错误或错误。 (尝试用一个很长的睡眠来替换循环,看看“泄漏”是否仍然存在。)这是否是由分析引起的缓慢泄漏可能无关紧要......因为您通常不运行生产代码启用了这么长时间的分析。


注意:不保证调用System.gc() 会导致GC 运行。阅读 javadoc。

【讨论】:

  • 嗨,斯蒂芬,感谢您的详细解释。我同意即使 System.gc() 运行,它也可能不会导致 GC 运行,但是我担心的一个问题是,如果我注释掉 System.out.println(i),并且只运行 while 循环增加语句,线程也休眠,消耗的内存在增加。
  • @VinaySathyanarayana - 我给了你一些可能的解释。请参阅要点。 IMO,最有可能的是它是分析器本身的产物。
【解决方案3】:

这是由于控制台中显示的文本,以及整数的大小(一点点)。

Java 打印函数使用 8 位 ASCII,因此 56000 次打印一个数字,每个字符 8 个字节很快就会占用内存。

【讨论】:

  • 没有。这不是由于控制台中显示的文本。如果我删除 println 并运行程序,内存使用量仍然会继续增加。你可以检查一下。 :)
【解决方案4】:

我在这段代码中没有看到任何内存泄漏。您应该了解 Java 中的垃圾收集器是如何工作的以及它的策略。基本上说 GC 在需要之前不会清理 - 如特定策略所示。

您也可以尝试拨打System.gc()

对象可能是在两个 Java Core 函数中创建的。

【讨论】:

    【解决方案5】:

    按照本教程查找内存泄漏:Analyzing Memory Leak in Java Applications using VisualVM。您必须在开始时制作应用程序的快照,并在一段时间后制作另一个快照。使用 VisualVM,您可以执行此操作并将其与快照进行比较。

    【讨论】:

    • 我也尝试运行 gc。 1.内存使用没有下降。即使我执行了 gc,它也可能在需要时运行。 2.假设我有一个更大的程序,如何确定是程序内存泄漏还是gc时间间隔问题?
    • @VinaySathyanarayana:请查看我更改后的答案。
    【解决方案6】:

    尝试将 JVM 内存上限设置得如此低,以致可能的泄漏会导致它耗尽内存。

    如果使用的内存达到该限制并继续愉快地工作,那么垃圾收集就可以完成它的工作。

    如果它爆炸了,那么你就有一个真正的问题......

    【讨论】:

      【解决方案7】:

      这似乎没有泄漏,因为分析器的图表也表明了这一点。图表在特定时间间隔后急剧下降,即执行 GC 时。如果图表继续稳步攀升,那将是一个泄漏。之后剩余的堆空间必须由 thread.sleep() 使用,并且(如上面的答案之一所述)来自探查器的某些代码。

      您可以尝试运行位于%JAVA_HOME%/bin 的 VisualVM 并在其中分析您的应用程序。它还为您提供了随意执行 GC 的选项以及更多选项。

      我注意到我使用的 VisualVM 的更多功能正在消耗更多内存(高达 10MB)。所以这个增加,它也必须来自你的分析器,但它仍然不是泄漏,因为空间是在 GC 上回收的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-03
        • 2011-02-20
        • 2013-08-12
        • 1970-01-01
        相关资源
        最近更新 更多