为什么64位JVM在达到xmx之前会丢失内存?

时间:2011-10-25 05:01:00

标签: java jvm 64-bit jvm-arguments

我正在努力解决java应用程序的大量内存需求。

为了解决更多内存,我已经切换到64位JVM并使用大型xmx。 但是,当xmx高于2GB时,应用程序似乎比预期更早耗尽内存。 当运行xmx为2400M并查看来自-verbosegc的GC信息时,我得到了......

[Full GC 2058514K->2058429K(2065024K), 0.6449874 secs] 

...然后它会抛出一个内存不足的异常。 我希望它在内存不足之前将堆增加到2065024K以上。

在一个简单的例子中,我有一个测试程序,它在循环中分配内存并打印出来自Runtime.getRuntime().maxMemory()Runtime.getRuntime().totalMemory()的信息,直到它最终耗尽内存。

在一系列xmx值上运行此操作时,Runtime.getRuntime().maxMemory()报告的内容比xmx少10%,总内存不会超过Runtime.getRuntime().maxMemory()的90%。

我正在使用以下64位jvm:

java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)

以下是代码:

import java.util.ArrayList;

public class XmxTester {


private static String xmxStr;

private long maxMem;
private long usedMem;
private long totalMemAllocated;
private long freeMem;


private ArrayList list;

/**
 * @param args
 */
public static void main(String[] args) {

xmxStr = args[0];
XmxTester xmxtester = new XmxTester();
}

public XmxTester() {

byte[] mem = new byte[(1024 * 1024 * 50)];

list = new ArrayList();
while (true) {
    printMemory();
    eatMemory();
}

}

private void eatMemory() {
// TODO Auto-generated method stub
byte[] mem = null;
try {
    mem = new byte[(1024 * 1024)];
} catch (Throwable e) {
    System.out.println(xmxStr + "," + ConvertMB(maxMem) + ","
        + ConvertMB(totalMemAllocated) + "," + ConvertMB(usedMem)
        + "," + ConvertMB(freeMem));

    System.exit(0);
}

list.add(mem);

}

private void printMemory() {
maxMem = Runtime.getRuntime().maxMemory();
freeMem = Runtime.getRuntime().freeMemory();
totalMemAllocated = Runtime.getRuntime().totalMemory();
usedMem = totalMemAllocated - freeMem;


}

double ConvertMB(long bytes) {

int CONVERSION_VALUE = 1024;

return Math.round((bytes / Math.pow(CONVERSION_VALUE, 2)));

}

}

我使用此批处理文件在多个xmx设置上运行它。它包括对32位JVM的引用,我想比较一个32位的jvm - 显然这个调用在xmx大于1500M时就会失败

@echo off
set java64=<location of 64bit JVM>
set java32=<location of 32bit JVM>
set xmxval=64


:start


SET /a xmxval  = %xmxval% + 64

 %java64%  -Xmx%xmxval%m  -XX:+UseCompressedOops -XX:+DisableExplicitGC XmxTester %xmxval%

%java32% -Xms28m -Xmx%xmxval%m   XmxTester %xmxval%

if %xmxval% == 4500 goto end
goto start
:end
pause

这会吐出一个csv,当放入excel时看起来像这样(为我这里糟糕的格式道歉)

32位

XMX  max mem  total mem   free mem  %of xmx used before out of mem exception
128  127  127  125  2  98.4%
192  191  191  189  1  99.0%
256  254  254  252  2  99.2%
320  318  318  316  1  99.4%
384  381  381  379  2  99.5%
448  445  445  443  1  99.6%
512  508  508  506  2  99.6%
576  572  572  570  1  99.7%
640  635  635  633  2  99.7%
704  699  699  697  1  99.7%
768  762  762  760  2  99.7%
832  826  826  824  1  99.8%
896  889  889  887  2  99.8%
960  953  953  952  0  99.9%
1024  1016  1016  1014  2  99.8%
1088  1080  1080  1079  1  99.9%
1152  1143  1143  1141  2  99.8%
1216  1207  1207  1205  2  99.8%
1280  1270  1270  1268  2  99.8%
1344  1334  1334  1332  2  99.9%

64位

128  122  122  116  6  90.6%
192  187  187  180  6  93.8%
256  238  238  232  6  90.6%
320  285  281  275  6  85.9%
384  365  365  359  6  93.5%
448  409  409  402  6  89.7%
512  455  451  445  6  86.9%
576  512  496  489  7  84.9%
640  595  595  565  30  88.3%
704  659  659  629  30  89.3%
768  683  682  676  6  88.0%
832  740  728  722  6  86.8%
896  797  772  766  6  85.5%
960  853  832  825  6  85.9%
1024  910  867  860  7  84.0%
1088  967  916  909  6  83.5%
1152  1060  1060  1013  47  87.9%
1216  1115  1115  1068  47  87.8%
1280  1143  1143  1137  6  88.8%
1344  1195  1174  1167  7  86.8%
1408  1252  1226  1220  6  86.6%
1472  1309  1265  1259  6  85.5%
1536  1365  1317  1261  56  82.1%
1600  1422  1325  1318  7  82.4%
1664  1479  1392  1386  6  83.3%
1728  1536  1422  1415  7  81.9%
1792  1593  1455  1448  6  80.8%
1856  1650  1579  1573  6  84.8%
1920  1707  1565  1558  7  81.1%
1984  1764  1715  1649  66  83.1%
2048  1821  1773  1708  65  83.4%
2112  1877  1776  1769  7  83.8%
2176  1934  1842  1776  66  81.6%
2240  1991  1899  1833  65  81.8%
2304  2048  1876  1870  6  81.2%
2368  2105  1961  1955  6  82.6%
2432  2162  2006  2000  6  82.2%

1 个答案:

答案 0 :(得分:13)

为什么会这样?

基本上,JVM / GC可以使用两种策略来决定何时放弃并抛出OOME。

  • 它可以继续前进和继续,直到垃圾收集之后没有足够的内存来分配下一个对象。

  • 它可以继续运行,直到JVM花费超过运行垃圾收集器的给定百分比的时间。

第一种方法存在以下问题:对于典型应用程序 JVM将花费越来越大的时间来运行GC,最终完成任务是徒劳的。

第二种方法存在可能过早放弃的问题。


此区域中GC的实际行为由JVM选项(-XX:...)控制。显然,默认行为在32位和64位JVM之间有所不同。这是有道理的,因为(直观地)64位JVM的“内存不足死亡螺旋”效应将持续更长时间并且更加明显。


我的建议是单独留下这个问题。除非你真的需要用内容填充每个内存的最后一个字节,否则JVM最好早点死掉并避免浪费大量时间。然后,您可以使用更多内存重新启动它并完成工作。

显然,您的基准是非典型的。大多数真正的程序根本不会尝试抓住所有堆。您的应用程序可能也是非典型的。但是,您的应用程序也可能遭受内存泄漏。如果是这种情况,你应该调查泄漏,而不是试图找出你不能使用所有内存的原因。


  

然而,我的问题主要是为什么它不尊重我的xmx设置。

尊重它! -Xmx是堆大小的上限,而不是决定何时放弃的标准。

  

我设置了一个2432M的XMX,但要求JVM返回其对最大内存返回2162M的理解。

它返回已使用的最大内存,而不是最大内存允许使用

  

为什么它'认为'最大内存比xmx少11%?

见上文。

  

此外,为什么当堆命中2006M时它没有将堆扩展到至少2162?

我认为这是因为JVM已经达到“花费太多时间收集垃圾”的门槛。

  

这是否意味着在64位JVM中,应该将XMX设置捏造为比预期最大值高11%?

不一般。软糖因素取决于您的应用。例如,具有较大对象流失率的应用程序(即,每单位有用工作创建和丢弃的对象越多)可能会更快地与OOME一起死亡。

  

我可以根据数据库大小预测需求,并有一个调整xmx的包装器,但是我有11%的问题,我的montioring建议应用程序需要2 GB,所以我设置了2.4GB xmx。然而,jvm不仅没有预期的400MB“净空”,而且只允许堆增长到2006M。

IMO,解决方案是在您当前添加的内容之上添加额外 20%(或更多)。假设您有足够的物理内存,为JVM提供更大的堆将减少整体GC开销并使您的应用程序运行得更快。

您可以尝试的其他技巧是将-Xmx和-Xms设置为相同的值并调整设置最大“垃圾收集时间”比例的调整参数。