Runtime.freeMemory() 函数存在问题 - Java

3

嘿,我正在使用以下代码测试 Runtime.freeMemory():

 Runtime runtime = Runtime.getRuntime();
 long freeMemory = runtime.freeMemory();

 // This is some arbitary factor that should work <= 1
 double factor = 0.8;

 int size = (int) (factor * freeMemory);
 byte[] testArray = new byte[size];

我正在创建一个大小接近freeMemory值的字节数组。不知为何,特别是当我将程序内存限制在约8MB左右时,对于任何大于0.55的因子,代码都会抛出OutOfMemory异常。这真的没有意义,毕竟freeMemory应该是freeMemory,我希望它有点误差,但实际上它的空闲量是它的两倍。

有什么建议吗? 谢谢

(注意,在我的测试中,我使用-Xmx8M等将可用内存限制为8MB或16MB。)


出于好奇,当您进行强制类型转换和乘法运算时,您溢出“int”的可能性有多大? - Matt Ball
这可能在factor值较大时失败的原因之一是您要求连续的字节数组。通过使用较小的块,您可以分配更多的总内存。 - clstrfsck
@spong - 如果我没记错的话,默认的GC行为将压缩对象,从而解决了这个问题,但是确实可能仍然会有一些影响。 - CurtainDog
1
@spong - 将数组分成较小的块实际上似乎完全解决了问题,现在我可以将我的空闲内存填充到大约其大小的0.98。谢谢! - Ricky Jones
5个回答

2
实际上,你的空闲内存分为两个代:一个是“年轻”代,另一个是“老年”代。如果你使用-verbose:gc -XX:+PrintGCDetails VM设置运行,你可以看到每个代所占用的内存量。我发现我可以完全填满我的老年代,但不能超过这个限制。
参考链接:http://java.sun.com/docs/hotspot/gc1.4.2/

没错:内存模型很复杂,实际行为取决于JVM的配置方式。 - CurtainDog

0

在使用runtime.gc()创建测试数组之前,请尝试进行垃圾回收。

如果您不是每次都创建全新的JVM,则可能会受到具有不同启动状态的影响。


我的程序可以处理大于1的数值,例如1.25。但是当数值过大时,会出现“堆空间”异常。


这里,也许你想用 'maxMemory()' 代替。

public class Mem2 {
    public static void main(String[] args) {


        Runtime runtime = Runtime.getRuntime();
        runtime.gc();

        long freeMemory = runtime.freeMemory();

        // This is some arbitary factor that should work <= 1
        double factor = 1.29;

        int size = (int) (factor * freeMemory);
        System.out.println("      freememory is " + freeMemory);
        System.out.println("            size is " + size);
        System.out.println("the total memory is " + runtime.totalMemory());
        System.out.println("  the max memory is " + runtime.maxMemory());
        byte[] testArray = new byte[size];
    }
}

输出:

      freememory is 84466864
            size is 108962254
the total memory is 85000192
  the max memory is 129957888

Process finished with exit code 0

看起来有大约20M我无法解释。

我认为totalMemory()是JVM当前分配的内存量,freeMemory()是已使用的内存量,maxMemory()是硬限制。


通过您代码的这个变体,您可以看到totalMemory()freememory()之间的相互作用。

public class Mem3 {

    public static void main(String[] args) {


        Runtime runtime = Runtime.getRuntime();

        for (int i = 0; true; i++) {
            runtime.gc();
            int size = i * 10000000;
            System.out.println("                 i is " + i);
            System.out.println("              size is " + size);
            System.out.println("b       freememory is " + runtime.freeMemory());
            System.out.println("b the total memory is " + runtime.totalMemory());
            System.out.println("b   the max memory is " + runtime.maxMemory());
            byte[] testArray = new byte[size];
            System.out.println("                array " + testArray.length);
            System.out.println("a       freememory is " + runtime.freeMemory());
            System.out.println("a the total memory is " + runtime.totalMemory());
            System.out.println("a   the max memory is " + runtime.maxMemory());
            System.out.println(" ");
        }

    }
}

如果你运行它并查看之前和之后的值,你就可以看到发生了什么。请注意在第6次和第7次迭代之间发生了什么:

                 i is 6
              size is 60000000
b       freememory is 84300496
b the total memory is 85000192
b   the max memory is 129957888
                array 60000000
a       freememory is 24300472
a the total memory is 85000192
a   the max memory is 129957888

                 i is 7
              size is 70000000
b       freememory is 84300496
b the total memory is 85000192
b   the max memory is 129957888
                array 70000000
a       freememory is 59258168
a the total memory is 129957888
a   the max memory is 129957888

我们可以从第6行看到,在分配了60M之后,还剩下约24M。然而,在第7行中,我们已经超过了一个阈值。更多的内存已被分配(请注意totalMemory),而freeMemory现在只有不到60M。

当我正常测试时,我也会得到大约1.3的因子,分配的默认内存为128M。但是,当我尝试使用-Xmx8M等在8MB或16MB左右进行测试时,出现了问题。问题似乎相当随机,在低内存水平下,它不允许我创建一个与空闲内存一样大的数组,但是在更高的内存水平下,你可以超过它。 - Ricky Jones
你可以超过免费内存的原因是,你的分配会触发垃圾回收,从而大幅增加可用内存的数量。如果有更多的内存,这一点尤为明显,因为垃圾回收会使更多的垃圾留在内存中更长时间。 - CurtainDog

0

这个答案基于主问题中 spong 的评论。

如果你将数组的创建拆分成许多较小的数组,而不是试图在一个块中创建它,你可以将空闲内存填充到约 0.98 的因子。

     Runtime runtime = Runtime.getRuntime();
     long freeMemory = runtime.freeMemory();

     // This is some arbitary factor that should work <= 1
     double factor = 0.8;

     int size = (int) (factor * freeMemory);


     int slice = 100;
     byte[][] testArrays = new byte[slice][1];
     for (int i = 1; i <= slice; i++) {
            testArrays[i-1] = new byte[size / slice];
            System.out.println("Allocated: " + i * size / slice);
     }

     System.out.println("Created! "+testArrays.length);

0
通过运行能够展示您系统中内存如何随时间分配的分析器,是掌握此技能的最佳途径。如果您使用的是 Eclipse,请确保已安装 TPTP,我相信其他大型IDEs都有类似的功能。

0

我认为这与堆分区的大小有关。当JVM启动时,它将把可用的堆内存分成许多“空间”;例如,有一个用于新创建对象的空间,一个用于老年对象的空间,一个“伊甸园”空间用于在第一次GC周期中幸存的对象等等。这些空间的实际大小是可调的(通过JVM选项),但“新对象”空间的大小很可能远小于8Mb。

然后,当您尝试分配包含报告的空闲内存55%的数组时,内存管理器需要在“新对象”空间中找到相应数量的连续内存。如果您遇到OOME,则是因为实际分区使得所需数量的连续内存不可用...即使在GC运行之后。

基本上,您正在尝试使用太小的堆来运行JVM以完成您要做的工作。通常原则是,Java的堆大小越大,应用程序运行速度越快(且问题越少)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接