Windows XP上Java的最大内存限制

105

我一直都能够为在32位Windows XP上运行的Java SE分配1400兆字节的内存(Java 1.4、1.5和1.6)。

java -Xmx1400m ...

今天我在一台新的Windows XP机器上尝试了相同的选项,使用了Java 1.5_16和1.6.0_07,并得到了以下错误:

Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.

经过试错,似乎在这台机器上我可以分配最多1200兆字节。

有什么想法为什么一台机器可以允许1400,而另一台只能允许1200?

编辑:该机器有4GB的RAM,大约有3.5GB可以被Windows识别。


在我的经验中,你会注意到在32位壳或64位壳中运行应用程序的最大差异,尽管64位WindowsXP系统很少见。 - djangofan
http://wiki.eclipse.org/Eclipse.ini - http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html#generation_sizing - Andrew
13个回答

130
请记住,Windows 有虚拟内存管理,JVM 只需要其地址空间中是连续的内存。因此,运行在系统上的其他程序不应该对堆大小产生影响。会阻碍你的是加载到地址空间中的 DLL。不幸的是,优化在链接期间最小化 DLL 重定位的 Windows 会使你更容易拥有一个碎片化的地址空间。在通常情况下,可能会占用你的地址空间的东西包括安全软件、CBT 软件、间谍软件和其他形式的恶意软件。导致变异的原因可能是不同的安全补丁、C 运行时版本等。设备驱动程序和其他内核组件有自己的地址空间(32 位空间的 4GB 中的另外 2GB)。
你可以尝试查看 JVM 进程中的 DLL 绑定,并尝试将 DLL 重新基址到更紧凑的地址空间中。虽然不好玩,但如果你非常需要这样做...
或者,你可以切换到 64 位 Windows 和 64 位 JVM。尽管其他人建议过,它会占用更多的 RAM,但你将拥有更多的连续虚拟地址空间,分配 2GB 的连续内存将很容易。

5
使用“进程资源管理器”查看dll文件在内存中被加载的位置。通常,某些已更新的驱动程序会将自己粘贴到您的地址空间中间。使用“REBASE”命令,您可以轻松将其推开,但请记住,该dll文件可能会再次更新并导致问题。 - brianegge
@Christopher,在32位的Windows XP上是否可以使用64位的JVM? - Pacerier
@Pacerier 抱歉,我错过了你的问题。据我所知,这是不可能的。OS X 在 64 位用户空间和 32 位内核方面有一些技巧,但我没有听说 Windows 有这样的技巧。 - Christopher Smith
@ChristopherSmith,顺便说一下,你提到“系统上运行的其他程序不应该对堆大小产生影响”。如果是这样,那么我们如何解释这个结果:https://dev59.com/U2ox5IYBdhLWcg3wSCfk#YbLlnYgBc1ULPQZF7_45? - Pacerier
@brianegge 你如何使用进程资源管理器查看已加载的dll? - speedplane
在Unix系统上使用共享库是否会导致类似的问题? - Nikita Tkachenko

51

这与连续的内存有关。

这里是我在网上找到的一些信息,供之前询问过这个问题的某人参考,据称来自“VM之神”:

我们需要在堆中获得一个连续的内存区域的原因是,我们有一堆侧面数据结构,它们通过从堆的开头开始的(按比例缩放的)偏移量进行索引。例如,我们使用“卡标记数组”跟踪对象引用更新,该数组对于每个512字节的堆都有一个字节。当我们在堆中存储引用时,我们必须标记相应的卡标记数组中的字节。我们将存储的目标地址右移,然后使用它来索引卡标记数组。在Java中无法完成的有趣寻址算术游戏,您可以(必须)在C ++中玩。

通常,我们不会遇到获得适度连续区域的问题(在Windohs上最多约1.5GB,在Solaris上最多约3.8GB。你的情况可能有所不同)。在Windohs上,问题主要是在JVM启动之前加载了一些库,这些库破坏了地址空间。使用/3GB开关无法重新定位这些库,因此它们对我们仍然是问题。

我们知道如何创建分块堆,但是使用它们会有一些开销。在32位JVM中,我们更需要更快的存储管理,而不是更大的堆。如果您真的想要大型堆,请切换到64位JVM。我们仍然需要连续的内存,但在64位地址空间中更容易获得。


这非常有趣。我一直在问自己为什么是1500 MB,现在我明白了,谢谢! - Tim Büthe
3
很抱歉追问一个老问题,但这是我目前看到的最好的答案。 但是如果JVM无法获得最大堆大小,为什么会在启动时失败?它不应该安静地选择在最小值以上的最佳大小吗? - Stroboskop

20

Windows下Java堆大小的限制如下:

  • 32位Java最大可能的堆大小:1.8 GB
  • 建议32位Java堆大小限制为1.5 GB(或使用/3GB选项可达到1.8 GB

这并不能帮助您获得更大的Java堆,但现在您知道您无法超越这些值。


10

Oracle JRockit可以处理非连续堆,配合/3GB开关在Windows 2003/XP上可达到2.85 GB的Java堆大小。碎片化似乎对Java堆的最大容量产生很大的影响。


6

JVM需要连续的内存,取决于其他正在运行的程序、之前运行的程序以及Windows如何管理内存,您可能能够获得高达1.4GB的连续内存。我认为64位Windows将允许更大的堆。


2
我认为现代操作系统为其模拟连续内存。自从80486以来,x86架构支持分页,使得重新排列物理内存变得容易。 - Mnementh
3
Mnemeth说:首先,WINAPI中有一个特定的API(AllocateUserPhysicalPages)供高级工具如数据库和虚拟机使用,这些工具最好自己管理内存,不需要Windows干预。其次,分页是80386保护模式特性,而不是80486。 - Tamas Czinege

6
Sun的JVM需要连续的内存。因此,可用内存的最大量由内存碎片化所决定。特别是驱动程序的dll在加载到某个预定义的基地址时,往往会导致内存碎片化。因此,您的硬件及其驱动程序决定了您可以获得多少内存。
以下是两个Sun工程师发表声明的来源:论坛 博客 也许可以尝试另一个JVM?您是否尝试过Harmony?我认为他们计划允许非连续内存。

但是我能够在一台只有1GBRAM(加上虚拟内存)的机器上分配1300MB。我的2GB RAM机器(也带有虚拟内存)只能分配1200MB。 - Steve Kuo
和谐已经死了,不是吗? - Pacerier
是的:“Apache Harmony自2011年11月16日起已经在Apache软件基金会退役。” - bobbel

3
我认为这更多与Windows的配置有关,正如这个回复所暗示的那样:Java -Xmx选项 更多测试:我能够在一个只有768MB物理内存(加上虚拟内存)的旧Windows XP机器上分配1300MB。在我的2GB RAM机器上,我只能得到1220MB。在其他各种公司机器上(带有较旧的Windows XP),我能够获得1400MB。限制为1220MB的机器相当新(刚从戴尔购买),因此可能具有更新(和更加臃肿的)Windows和DLL(它正在运行Window XP专业版2002 SP2)。

这也可能受到您的虚拟内存设置的影响。 - skaffman
我测试的所有机器都具有虚拟内存,至少是物理内存的两倍。 - Steve Kuo
请注意,您真的不希望在Java中使用虚拟内存,因为GC性能会变得非常糟糕。内存的大小取决于已加载的DLL以及对内存造成的碎片化。 - kohlerm

2
当我从一个(内存有限的)virtuozzo VPS运行Java程序时,我收到了这个错误消息。我没有指定任何内存参数,并发现我必须显式地设置一个量作为默认值可能太高了。例如:-Xmx32m(显然需要根据您运行的程序进行调整)。
我在这里放置这个信息,以防其他人在没有指定像提问者那样大量的内存时收到上述错误消息。

1

大家似乎都在谈论连续内存,但却忽略了一个更紧迫的问题。

即使在100%连续内存分配的情况下,你也不能在32位Windows操作系统(*默认情况下)上拥有2 GiB的堆大小。这是因为32位Windows进程无法寻址超过2 GiB的空间。

Java进程将包含perm gen(Java 8之前),每个线程的堆栈大小,JVM/库开销(几乎随着每个版本的构建而增加) 除了堆以外的所有内容

此外,JVM标志及其默认值在不同版本之间也会发生变化。只需运行以下命令,您就可以获得一些想法:

 java -XX:+PrintFlagsFinal

许多选项会影响堆内外存储的分配。这将使你拥有更多或更少的 2 GiB 内存可供使用...
为了重复利用我此处的答案(关于 Tomcat,但适用于任何 Java 进程):
Windows操作系统默认情况下限制32位进程的内存分配总量为2 GiB。由于还有其他内存分配给进程(JVM/库开销、perm gen空间等),因此您只能分配约1.5 GiB堆空间。其他现代操作系统(比如Linux)允许32位进程使用4 GiB可寻址空间中的全部(或大部分)。尽管如此,64位Windows操作系统可以配置以将32位进程的限制增加到4 GiB(32位上为3 GiB)。详情请参见:http://msdn.microsoft.com/en-us/library/windows/desktop/aa366778(v=vs.85).aspx

1
这个答案只解释了为什么他可能只能分配2GB,而不是为什么他在一台电脑上可以分配1.4GB,而在另一台电脑上只能分配1.2GB。他没有达到你在这里提到的1.5GB、2GB或4GB的限制。 - vapcguy
1
JVM标志的段落在一定程度上解释了为什么不同版本之间的内存可能会有所不同。此外,请注意我的观点,即堆设置始终是总进程大小的(大)分数 - 因此,低于该设置的设置仍可能达到2 GiB _process_限制 - 另一个可能受到连续内存分配的限制。 - Michael
或者可能是关于他正在进行的1.4GB分配上的1.5GB限制。现在更有意义了-感谢您的澄清。 - vapcguy

1

Sun 的 JDK/JRE 需要一段连续的内存空间,如果你分配了一个巨大的块。

操作系统和初始化的应用程序往往在加载过程中分配各种小块内存,导致可用 RAM 发生碎片。如果没有连续的内存块可用,Sun JDK 就无法使用它。而被 Oracle 收购的 Bea 公司的 JRockit 可以从这些碎片中分配内存。


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