一个64位操作系统上32位JVM的最大Java堆大小是多少?

118

这个问题不是关于32位操作系统的最大堆大小,因为32位操作系统的可寻址内存大小最大为4GB,而JVM的最大堆大小取决于可以保留多少连续的空闲内存。

我更想知道在64位操作系统上运行的32位JVM的最大(理论和实际可达到)堆大小。基本上,我正在寻找类似于SO中相关问题答案中的数字的答案。

至于为什么使用32位JVM而不是64位JVM,原因不是技术上的,而是行政/官僚主义的 - 在生产环境中安装64位JVM可能为时已晚。

17个回答

96

你可以询问Java运行时:

public class MaxMemory {
    public static void main(String[] args) {
        Runtime rt = Runtime.getRuntime();
        long totalMem = rt.totalMemory();
        long maxMem = rt.maxMemory();
        long freeMem = rt.freeMemory();
        double megs = 1048576.0;

        System.out.println ("Total Memory: " + totalMem + " (" + (totalMem/megs) + " MiB)");
        System.out.println ("Max Memory:   " + maxMem + " (" + (maxMem/megs) + " MiB)");
        System.out.println ("Free Memory:  " + freeMem + " (" + (freeMem/megs) + " MiB)");
    }
}

这将根据默认堆分配报告“最大内存”。因此,您仍需要使用-Xmx(在HotSpot上)。我发现在运行Windows 7企业版64位时,我的32位 HotSpot JVM可以分配高达1577MiB:
[C:scratch]> java -Xmx1600M MaxMemory
VM初始化期间发生错误
无法为对象堆保留足够的空间
无法创建Java虚拟机。
[C:scratch]> java -Xmx1590M MaxMemory
总内存:2031616(1.9375 MiB)
最大内存:1654456320(1577.8125 MiB)
可用内存:1840872(1.75559234619 MiB)
[C:scratch]>
而在同一操作系统上使用64位 JVM,当然更高(约为3TiB)
[C:scratch]> java -Xmx3560G MaxMemory
VM初始化期间发生错误
无法为对象堆保留足够的空间
[C:scratch]> java -Xmx3550G MaxMemory
总内存:94240768(89.875 MiB)
最大内存:3388252028928(3184151.84297 MiB)
可用内存:93747752(89.4048233032 MiB)
[C:scratch]>
正如其他人已经提到的那样,它取决于操作系统。

对于64位主机操作系统,如果JVM为32位,则仍将取决于上述演示的大多数情况。

-- 更新20110905:我只想指出一些其他的观察/细节:

  • 我运行这个程序的硬件是64位,安装了6GB的实际内存。操作系统为Windows 7 Enterprise,64位
  • Runtime.MaxMemory 实际可分配的最大内存也取决于操作系统的工作集。我曾经在同时运行VirtualBox时运行过这个程序,并发现我不能成功地使用-Xmx1590M启动HotSpot JVM,只能减小内存设置。这也意味着,根据您在某一时刻的工作集大小,您可能会获得超过1590M的内存(尽管我仍然认为对于32位而言,由于Windows的设计,它将不会超过2GiB)

14
我喜欢你真正进行了测试,而不是胡乱猜测。 - Jim Hurne
3
@djangofan是正确的。该程序应该除以1,048,576(1024*1024或2^20),而不是104,856。但这只是一个显示问题。从命令中可以看出,他只是尝试将最大堆大小设置为1,590 MiB。 - Jim Hurne
1
非常棒的答案(我想说是最佳答案),附带代码示例和详细说明,覆盖所有操作系统。 - gcode
3
针对我之前的评论进行跟进:jdocs(至少适用于Windows)指定 -Xmx 和 -Xms 参数必须给定一个是1024的倍数的值... 我不确定1590是否是,所以我认为应该预期会出现奇怪的结果。 - gcode
4
TGP1994发现的很好。我认为,因为我已经指定了“M”和“G”倍数,所以大小(以字节为单位)将自动转换为1024的倍数。例如,1590M将被解析为1590 *(1024 * 1024)= 1667235840字节(1628160KiB-当然是1024的整数倍)。 - mike
显示剩余7条评论

76

32位的Java虚拟机只能使用单个大块内存并使用原始指针,因此不能超过4 Gb(这是32位限制,也适用于指针)。这包括Sun和我非常确定的IBM实现。我不知道例如JRockit或其他人是否具有其32位实现的大内存选项。

如果您预计会遇到此限制,强烈建议您启动并行轨道,为生产环境验证64位Java虚拟机,以便在32位环境崩溃时准备好该解决方案。否则,您将不得不在压力下完成这项工作,这是不好的。


编辑2014-05-15:Oracle FAQ:

32位JVM的最大理论堆限制为4G。由于各种附加约束条件,例如可用交换、内核地址空间使用、内存碎片和VM开销,实际上限制可能要低得多。在大多数现代32位Windows系统上,最大堆大小将在1.4G至1.6G之间。在32位Solaris内核上,地址空间限制为2G。在运行32位虚拟机的64位操作系统上,最大堆大小可以更高,在许多Solaris系统上接近4G。

(http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_heap_32bit)


1
因为签名,大小不是大约2GB吗?还是只有Sun JVM才是这样的? - Sebastian Ganslandt
9
指针不带符号——谈论负内存位置是没有意义的。 - Thorbjørn Ravn Andersen
12
不,它不是2GB,因为有符号位。然而,4GB的地址空间中的一部分是留给操作系统内核的。在正常的Windows消费者版本中,限制是2GB。在Linux和Windows服务器版本(32位)中,每个进程的限制是3GB。 - Jesper
3
@Jesper,我想知道在64位操作系统上运行的32位JVM是否可以使用完整的4GB地址空间? - Thorbjørn Ravn Andersen
1
@zzy 尝试添加 -XX:+AlwaysPreTouch,你所请求的是虚拟空间,一旦启用该键 - 它将失败。 - Eugene
显示剩余13条评论

17

你没有说明是哪个操作系统。

在 Windows 下(对于我的应用程序 - 一个长期运行的风险管理应用程序),我们发现在 Windows 32 位上最多只能使用 1280MB。我怀疑在64位操作系统下运行 32 位 JVM 不会有任何不同。

我们将该应用程序移植到 Linux 并在64位硬件上运行32位 JVM,很容易地运行了一个 2.2GB 的虚拟机。

你可能会遇到的最大问题是 GC,这取决于你使用内存的方式。


我更想了解Solaris 10的限制,但那只是针对我手头的问题。也想了解其他操作系统的限制,以备不时之需 :) - Vineet Reynolds
我对Solaris不太确定。我预计虚拟机的大小会相当大,我的Java在Solaris上的经验是几年前的事了。而且作为Sun硬件上的Sun OS上的Sun VM - 事情运行得相当顺利。我还听说,在Solaris下比Linux/Windows下的GC问题要少一些。 - Fortyrunner
这是哪个Windows版本?我相信Windows 32位的服务器版本可以更好地处理大量内存。 - Thorbjørn Ravn Andersen
啊,终于提到操作系统了;-)您安装了64位内核吗? - Thorbjørn Ravn Andersen
它是Win2k服务器。移动到Win2k3(事情进展缓慢...)太晚了,我们改用Linux。 - Fortyrunner
你提到GC问题,获得了点赞 - 这对于更大的虚拟机尺寸非常重要。 - shadit

15

来自4.1.2堆大小

"对于32位进程模型,进程的最大虚拟地址大小通常为4 GB,尽管某些操作系统将其限制为2 GB或3 GB。堆的最大大小通常为-Xmx3800m(2 GB限制为1600m),但实际限制取决于应用程序。对于64位进程模型,最大值基本上是无限的。"

在这里找到了一个很好的答案:Windows XP上Java的最大内存


14

我们最近有一些相关经验。我们最近从Solaris(x86-64版本5.10)移植到Linux(RedHat x86-64),发现在Linux上,32位JVM进程可用的内存比Solaris少。

对于Solaris来说,这几乎等同于4GB(http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_heap_32bit)。

过去几年里,我们使用-Xms2560m -Xmx2560m -XX:MaxPermSize=512m -XX:PermSize=512m在Solaris上运行我们的应用程序,没有任何问题。尝试将其移动到Linux上时,我们遇到了随机的内存不足错误。我们只能使用-Xms2300 -Xmx2300来保证应用程序能够始终启动。然后我们得到了支持团队的建议。

在Linux上,32位进程的最大可寻址地址空间为3GB(3072MB),而在Solaris上则是完整的4GB(4096MB)。


支持团队给出答案的原因在于进程可寻址内存的大小。这取决于Linux内核,甚至取决于硬件。理论上,可寻址内存限制为2^32 = 4G(Java堆大小会小于此值)。但是,使用hugemem和PAE可以(理论上)扩展此限制;我尚未尝试过这样做。 - Vineet Reynolds

9
在64位操作系统上运行32位JVM的限制与在32位操作系统上运行32位JVM的限制完全相同。毕竟,32位JVM将在32位虚拟机中运行(在虚拟化意义上),因此它不知道自己正在64位OS/机器上运行。
在64位操作系统上运行32位JVM与在32位操作系统上运行32位JVM相比的一个优势是可以使用更多物理内存,因此会更少地遇到交换/分页问题。然而,只有在有多个进程时,这种优势才能真正实现。

1
根据硬件和虚拟化方式的不同,可能会有轻微差异。通常,4GB可寻址空间中的一部分用于内存映射设备。虚拟化层可能与机器上的物理设备具有不同的内存占用情况。 - Eric J.
8
不完全正确。在64位机器上,JVM有更多的空间,因为32位寻址空间不必与操作系统或硬件接口共享。 - Thorbjørn Ravn Andersen
2
LG:我不同意你的原始答案。操作系统内核和它映射的任何硬件和总线地址空间将占用大量地址空间,虽然这些并没有映射到用户程序中,但它确实会在操作系统设置完成后减少剩余的数量。这在4GB 32位空间中占据了相当大的一部分。传统上,这意味着大约25%-75%的4GB对用户进程不可用。 :-) 内核黑客 - DigitalRoss
@DR,Eric,Thorbjørn +1。这正是32位操作系统无法使用所有可用4GB RAM的原因。 - Vineet Reynolds
劳伦斯·冈萨尔维斯,我鼓励你查找在现代64位操作系统下可以直接提供给32位进程的内存容量。 - Thorbjørn Ravn Andersen
显示剩余3条评论

6
关于为什么使用32位JVM而不是64位,原因并非技术上的,而是行政/官僚主义...当我在BEA工作时,我们发现平均应用程序在64位JVM中实际上比在32位JVM中运行时速度更慢。在某些情况下,性能下降高达25%。因此,除非您的应用程序确实需要所有这些额外的内存,否则最好设置更多的32位服务器。据我回忆,BEA专业服务人员遇到使用64位的三个最常见的技术理由是:1) 应用程序正在操作多个大型图像;2) 应用程序正在进行大量的数学计算;3) 应用程序具有内存泄漏,客户是政府合同中的主要人物,并且他们不想花费时间和成本来跟踪内存泄漏。(使用巨大的内存堆会增加MTBF,而主要人物仍将得到报酬)。

很好,你提供了第一手的建议,BEA的前员工 :) - emecas

4

以下是在Solaris和Linux 64位下进行的一些测试。

Solaris 10 - SPARC - T5220机器,拥有32 GB RAM(大约有9 GB可用)

$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3750m MaxMemory
Error occurred during initialization of VM
Could not reserve space for ObjectStartArray
$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3700m MaxMemory
Total Memory: 518520832 (494.5 MiB)
Max Memory:   3451912192 (3292.0 MiB)
Free Memory:  515815488 (491.91998291015625 MiB)
Current PID is: 28274
Waiting for user to press Enter to finish ...

$ java -version
java version "1.6.0_30"
Java(TM) SE Runtime Environment (build 1.6.0_30-b12)
Java HotSpot(TM) Server VM (build 20.5-b03, mixed mode)

$ which java
/usr/bin/java
$ file /usr/bin/java
/usr/bin/java: ELF 32-bit MSB executable SPARC Version 1, dynamically linked, not stripped, no debugging information available

$ prstat -p 28274
   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/NLWP
28274 user1     670M   32M sleep   59    0   0:00:00 0.0% java/35

顺便提一下:Java启动时似乎不会分配太多实际内存。每个实例大约只需要100 MB的内存(我启动了10个实例)

Solaris 10 - x86 - VMWare虚拟机,8 GB RAM(大约有3 GB可用*)

这里说的3GB可用内存并不完全准确。有一块很大的RAM被ZFS缓存使用了,但是我没有root权限去检查具体有多少。

$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3650m MaxMemory
Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.

$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3600m MaxMemory
Total Memory: 516423680 (492.5 MiB)
Max Memory:   3355443200 (3200.0 MiB)
Free Memory:  513718336 (489.91998291015625 MiB)
Current PID is: 26841
Waiting for user to press Enter to finish ...

$ java -version
java version "1.6.0_41"
Java(TM) SE Runtime Environment (build 1.6.0_41-b02)
Java HotSpot(TM) Server VM (build 20.14-b01, mixed mode)

$ which java
/usr/bin/java

$ file /usr/bin/java
/usr/bin/java:  ELF 32-bit LSB executable 80386 Version 1 [FPU], dynamically linked, not stripped, no debugging information available

$ prstat -p 26841
   PID USERNAME  SIZE   RSS STATE  PRI NICE      TIME  CPU PROCESS/NLWP
26841 user1     665M   22M sleep   59    0   0:00:00 0.0% java/12

RedHat 5.5 - x86 - VMWare虚拟机,配备4GB内存(大约使用了3.8GB,其中缓冲区占用200MB,高速缓存占用3.1GB,因此还剩下3GB可用内存)

$ alias java='$HOME/jre/jre1.6.0_34/bin/java'

$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3500m MaxMemory
Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.

$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3450m MaxMemory
Total Memory: 514523136 (490.6875 MiB)
Max Memory:   3215654912 (3066.6875 MiB)
Free Memory:  511838768 (488.1274871826172 MiB)
Current PID is: 21879
Waiting for user to press Enter to finish ...

$ java -version
java version "1.6.0_34"
Java(TM) SE Runtime Environment (build 1.6.0_34-b04)
Java HotSpot(TM) Server VM (build 20.9-b04, mixed mode)

$ file $HOME/jre/jre1.6.0_34/bin/java
/home/user1/jre/jre1.6.0_34/bin/java: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), for GNU/Linux 2.2.5, not stripped

$ cat /proc/21879/status | grep ^Vm
VmPeak:  3882796 kB
VmSize:  3882796 kB
VmLck:         0 kB
VmHWM:     12520 kB
VmRSS:     12520 kB
VmData:  3867424 kB
VmStk:        88 kB
VmExe:        40 kB
VmLib:     14804 kB
VmPTE:        96 kB

使用JRE 7的同一台计算机

$ alias java='$HOME/jre/jre1.7.0_21/bin/java'

$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3500m MaxMemory
Error occurred during initialization of VM
Could not reserve enough space for object heap
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

$ java -XX:PermSize=128M -XX:MaxPermSize=256M -Xms512m -Xmx3450m MaxMemory
Total Memory: 514523136 (490.6875 MiB)
Max Memory:   3215654912 (3066.6875 MiB)
Free Memory:  511838672 (488.1273956298828 MiB)
Current PID is: 23026
Waiting for user to press Enter to finish ...

$ java -version
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) Server VM (build 23.21-b01, mixed mode)

$ file $HOME/jre/jre1.7.0_21/bin/java
/home/user1/jre/jre1.7.0_21/bin/java: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped

$ cat /proc/23026/status | grep ^Vm
VmPeak:  4040288 kB
VmSize:  4040288 kB
VmLck:         0 kB
VmHWM:     13468 kB
VmRSS:     13468 kB
VmData:  4024800 kB
VmStk:        88 kB
VmExe:         4 kB
VmLib:     10044 kB
VmPTE:       112 kB

这里有一些我们一直缺少的平台的有用测试结果。使用文件和内存转储的工作做得很好。 - mike

4

3

应该会好很多

对于在64位主机上运行的32位JVM,我想剩下的堆空间将是在JVM、它自己的DLL和任何32位兼容性附加组件加载之后剩余的未碎片化虚拟空间。我猜测可能有3GB的空间可用,但这取决于您在32位主机方面的表现如何。

此外,即使您可以创建一个3GB的巨大堆,您可能也不希望这样做,因为这会导致GC暂停变得潜在麻烦。一些人只是运行更多的JVM来使用额外的内存,而不是一个巨大的JVM。我想他们正在调整JVM以更好地处理巨大的堆。

很难准确知道您可以做到多好。我猜您的32位情况可以通过实验轻松确定。它肯定很难在抽象层面上预测,因为许多因素都会影响它,特别是因为32位主机上可用的虚拟空间相当受限。堆确实需要存在于连续的虚拟内存中,因此dll的地址空间的碎片化以及操作系统内核对地址空间的内部使用将决定可能的分配范围。

操作系统将使用一些地址空间来映射HW设备和它自己的动态分配。虽然此内存未映射到Java进程地址空间中,但操作系统内核无法同时访问它和您的地址空间,因此它会限制任何程序的虚拟空间大小。

加载DLL取决于实现和JVM的版本。加载操作系统内核取决于大量事项,例如版本、硬件、自上次重新启动以来已映射的内容数量等等。

总结

我敢打赌,在32位环境下您可以获得1-2GB,在64位环境下约为3GB,因此总体改进约为2倍


1
很不幸的是,我没有一个64位环境可以随意尝试Xmx标记。我知道有一个可用的巨大(32 * n)GB内存,但超出了范围。这就是为什么我想知道在没有正常面对32位世界中的所有限制的情况下,32位JVM将如何运行。 - Vineet Reynolds
1
64位的≅ 3GB听起来差不多就是这样。Thorbjørn已经解释了为什么理论上不能超过4GB。太遗憾了,我不能接受两个答案。 - Vineet Reynolds
64位支持的内存远超过3G,1000G也不是问题。在回应这个“猜测”https://dev59.com/P3I95IYBdhLWcg3w7CjL时,点赞绝对是错误的做法。 - Ajax
@Ajax:读问题他说出于政治原因使用32位Java。因此,他在Java +库+解释器+所有其他开销的总和上受到4GB的限制。我的观点是,如果他使用64位内核而不是32位内核,他将遇到不同的限制。 - DigitalRoss
你自己说了,你的答案只是一个猜测。据我所知,猜测而不是测试总是一个坏主意。在64位Windows机器上运行32位jvm并不能帮助太多,因为你需要操作系统分配连续的内存,而系统加载的DLL限制你只能使用1.5-1.8G,无论架构如何。如果你有一个64位系统和大量的内存,jvm可能能够找到大量未使用的连续内存,但你不会因为操作系统是64位而神奇地获得2倍的好处。https://dev59.com/M3VC5IYBdhLWcg3w1E1q - Ajax
显示剩余4条评论

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