Java 6 过多的内存使用

2022-09-02 01:09:25

Java 6 消耗的内存是否比您预期的要多?

我有一个多年来一直在开发的应用程序,到目前为止,在我的特定测试配置中,它占用了大约30-40 MB;现在使用Java 6u10和11,它需要几百个活跃的时间。它会在50M到200M之间的任何地方反弹很多,当它闲置时,它会执行GC并立即丢弃内存。此外,它还会产生数百万个页面错误。所有这些都是通过Windows任务管理器观察到的。

因此,我在我的分析器(jProfiler)下并使用jVisualVM运行了它,并且它们都表明通常的中等堆和烫发使用量约为30M,即使完全处于活动状态进行负载测试周期也是如此。

所以我很困惑!它不仅仅是从Windows虚拟内存池请求更多内存 - 这显示为200M“内存使用情况”。

澄清:我想非常清楚地说明这一点 - 在Java VisualVM的18小时内观察到类堆和perm gen堆一直非常稳定。分配的易失性堆(eden和pregressed)在16MB处保持不变(它在前几分钟内达到),并且此内存的使用以从8MB到16MB均匀增长的完美模式波动,此时GC将其踢回8MB。在这 18 小时内,由于我运行了压力测试,系统处于恒定的最大负载状态。此行为是完全且一致的可重现性,在多次运行中可见。唯一的异常是,当这种情况发生在Windows上时,通过任务管理器观察到的从Windows中获取的内存从64MB到900 + MB到900 + MB到所有地方波动。

2008-12-18 更新:我使用 -Xms16M -Xmx16M 运行程序,没有任何明显的不利影响 - 性能很好,总运行时间大致相同。但从短期来看,内存使用量仍然达到峰值,约为180M。

2009-01-21更新:似乎答案可能在于线程数量 - 请参阅下面的答案。


编辑:我的意思是字面上有数百万个页面错误 - 在30M +的区域。

编辑:我有一台4G机器,所以200M在这方面并不重要。


答案 1

为了回应对Ran答案的评论中的讨论,这里有一个测试用例,证明JVM在某些情况下会将内存释放回操作系统:

public class FreeTest
{
    public static void main(String[] args) throws Exception
    {
        byte[][] blob = new byte[60][1024*1024];
        for(int i=0; i<blob.length; i++)
        {
            Thread.sleep(500);
            System.out.println("freeing block "+i);
            blob[i] = null;
            System.gc();
        }
    }
}

我看到JVM进程的大小在Java 1.4和Java 6 JVM(来自Sun)上达到40左右时减小。

您甚至可以使用-XX:MaxHeapFreeRatio和-XX:MinHeapFreeRatio选项调整确切的行为 - 该页面上的一些选项也可能有助于回答原始问题。


答案 2

我不知道页面错误。但是关于分配给Java的巨大内存:

  1. Sun 的 JVM 仅分配内存,从不解除分配内存(直到 JVM 死亡),只有在内部内存需求与分配内存之间的特定比率低于(可调)值后才解除分配内存。JVM 从 -Xms 中指定的量开始,并且可以扩展到 -Xmx 中指定的量。我不确定默认值是什么。每当JVM需要更多内存(新对象/基元/数组)时,它就会从操作系统中分配整个块。但是,当需求消退时(暂时的需求,另请参阅2),它不会立即将内存释放回操作系统,而是将其保留到自身,直到达到该比率。我曾经被告知JRockit表现得更好,但我无法验证它。

  2. Sun 的 JVM 基于多个触发器运行完整的 GC。其中之一是可用内存量 - 当它下降太多时,JVM会尝试执行完整的GC以释放更多内存。因此,当从操作系统分配更多内存(临时需求)时,完整GC的机会就会降低。这意味着,虽然您可能会看到30Mb的“活动”对象,但可能还有更多的“死”对象(无法访问),只是等待GC发生。我知道youkit有一个很好的视图,叫做“死物”,你可能会看到这些“剩菜”。

  3. 在“-server”模式下,Sun的JVM以并行模式运行GC(与较旧的串行“停止世界”GC相反)。这意味着,虽然可能有垃圾需要收集,但由于其他线程占用了所有可用的 CPU 时间,因此可能不会立即收集垃圾。它将在内存耗尽之前收集(嗯,有点,请参阅 http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html),如果可以从操作系统分配更多内存,则可能是在GC运行之前。

总之,大型初始内存配置和短脉冲串创建大量短期对象可能会创建如上所述的方案。

编辑:将“从不脱壳”改为“仅在达到比率后”。