Java启动失败 - 无法为对象堆保留足够的空间

50

背景

我们有大约20个Linux服务器。其中一些运行Suse,另一些运行Redhat。它们全部共享NAS空间,其中包含以下3个文件夹:

  • /NAS/app/java - 指向Java JDK安装的符号链接。目前版本为1.5.0_10。
  • /NAS/app/lib - 指向我们应用程序的版本的符号链接。
  • /NAS/data - 存储我们输出的目录。

我们的所有机器都有2个处理器(超线程)和4GB物理内存以及4GB交换空间。我们限制每台机器在给定时间内可以处理的“作业”数量为6个(这个数字可能需要更改,但这不会涉及到当前问题,请暂时忽略它)。

我们的一些作业将最大堆大小设置为512MB,其他一些则将最大堆大小保留为2048MB。同样,我们意识到如果在堆大小设置为2048的情况下在同一台机器上启动6个作业,我们可能会超过可用内存,但据我们所知,这种情况尚未发生。

问题

偶尔会有一个作业立即失败,并显示以下消息:

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

我们过去常常将这归咎于同一台机器上同时运行的任务过多。问题发生得并不频繁(也许每月一次),所以我们只需重新启动它,一切就会好起来。

最近问题变得更加严重了。所有请求最大堆大小为2048m的工作几乎每次都失败,并需要重新启动几次才能完成。

我们已尝试在单个机器上手动执行,但结果相同。

调试

事实证明问题仅存在于我们的SuSE系统机器上。问题变得更加频繁是因为我们增加了更多的机器,而新机器都是SuSE系统。

在SuSE系统机器上执行'cat /proc/version',输出为:

Linux version 2.6.5-7.244-bigsmp (geeko@buildhost) (gcc version 3.3.3 (SuSE Linux)) #1 SMP Mon Dec 12 18:32:25 UTC 2005

在 RedHat 系统上执行 'cat /proc/version' 命令会得到以下输出:

Linux version 2.4.21-32.0.1.ELsmp (bhcompile@bugs.build.redhat.com) (gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-52)) #1 SMP Tue May 17 17:52:23 EDT 2005

'uname -a' 在两种类型的机器上都会给我们以下信息:

UTC 2005 i686 i686 i386 GNU/Linux

机器上没有运行任务,也没有其它进程占用太多内存。当前正在运行的所有进程可能使用了总计100mb。

'top'目前显示如下:

Mem:   4146528k total,  3536360k used,   610168k free,   132136k buffers
Swap:  4194288k total,        0k used,  4194288k free,  3283908k cached

'vmstat' 目前显示如下:

procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----
r  b   swpd   free   buff  cache   si   so    bi    bo   in    cs us sy id wa
0  0      0 610292 132136 3283908    0    0     0     2   26    15  0  0 100  0

如果我们使用以下命令行启动作业(最大堆为1850mb),它会顺利开始:

java/bin/java -Xmx1850M -cp helloworld.jar HelloWorld
Hello World

如果我们将最大堆大小增加到1875mb,它就会失败:

java/bin/java -Xmx1875M -cp helloworld.jar HelloWorld
Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.

很明显,当前正在使用的内存是用于缓冲/缓存,这就是为什么“空闲”显示很少的原因。不清楚的是,为什么有一个神奇的1850mb线,超过这个线意味着Java无法启动。

任何解释都将不胜感激。


3
请参考https://dev59.com/M3VC5IYBdhLWcg3w1E1q 了解如何在Windows XP系统上设置Java最大内存限制。 - Michael Myers
你为什么有32位的刀片? :P - wsorenson
嗨Randyaa,你最后弄清楚是什么原因导致这个问题了吗?当我尝试启动WebLogic服务器时,我遇到了完全相同的错误...谢谢 - GavinWoods
1
很遗憾,我们实施了一些技术来减少内存使用量,并且能够将最大使用量降至约1GB。抱歉:( - Randyaa
+1 很详细的内容。 - xdhmoore
15个回答

21

你正在使用32位操作系统,因此由于这个原因你将会看到在总大小方面存在限制。其他答案已经更加详细地介绍了这一点,所以我避免重复他们的信息。

我们最近在服务器上注意到的一个行为是,在不指定最小堆大小(使用-Xms)的情况下指定最大堆大小(使用-Xmx),将导致Java的服务器VM立即尝试分配为最大堆大小所需的所有内存。当然,如果应用程序达到了该堆大小,您将需要相应量的内存。但是,很有可能,您的应用程序将从相对较小的堆开始,并且在稍后某个时间可能需要更大的堆。另外,指定最小堆大小将使您的应用程序可以以更小的堆启动,并逐渐增加该堆。

所有这些都不能帮助您增加最大堆大小,但我认为这可能会有所帮助,所以......


我已经提到过,我认为这与我们的32位操作系统无关。SuSE和RedHat机器都是32位的,但它们并不都有这个问题。至于您设置最小值的建议,我尝试了一下,但对我没有任何作用 :(。 - Randyaa

15

如其他回答所建议的那样,问题是由虚拟地址空间耗尽引起的。 32位Linux用户空间程序通常限制为3GB的AS;剩余的1GB由内核使用(原因是:由于顶部1GB是内核固定映射,因此在提供syscalls时不需要触及页表)。

然而,RHEL内核实现了所谓的4GB / 4GB分离,在运行时稍微增加开销的情况下(内核位于单独的4GB虚拟AS中),将完整的4GB AS提供给用户空间进程。


8

运行32位操作系统是一个错误,您应该尽快升级。

我不知道Java是否需要将其堆放在单个连续的块中,但如果需要,在32位计算机上要求1.8G的堆似乎很困难。您假设在JVM启动时有一个地址空间的块,几乎有一半空余。

取决于在此时加载的其他库,可能并没有这样的空间。库可以在任何地方分配内存,因此它可能会使您的地址空间碎片化,以至于1.8G的空间无法以一个块的形式使用。

在Linux 32位系统中,最多只有3G的地址空间可用。库和JVM本身也需要其中的一部分。


如果可以的话,我会换系统。但这不取决于我。我需要解决手头的问题,这就是为什么Redhat机器允许我们使用2500+,而SuSE机器限制我们只能使用1850的原因。 - Randyaa

4
似乎对于32位服务器存在JVM限制,无法克服(除非您找到一个不强加2GB或更少限制的特殊32位JVM)。
The Server Side上的这个主题有更多细节,包括几个在32位架构上测试各种JVM的人。IBM的JVM似乎允许额外100MB,但这并不能真正满足您的需求。

http://www.theserverside.com/discussions/thread.tss?thread_id=26347

"真正"的解决方案是使用64位服务器和64位JVM,以获得每个进程大于2GB的堆。然而,还要考虑使用64位JVM增加地址大小(不仅是可寻址空间)的影响。对于使用少于4GB内存的处理,可能会有性能和内存影响。
思考一下:这些工作中是否真的需要2GB的内存?是否有办法修改这些工作,使其在1.8GB内运行,以避免此限制成为问题?

1
我真的不认为这是32位问题。两台机器都运行着32位操作系统,但它们各自有不同的限制。RedHat服务器的限制大约是2500+MB左右,而SuSE服务器的限制则大约为1850Mb左右。 - Randyaa
关于更改系统或“真正”需要2GB...我们计划进行一些更改以减少内存印记,但是目前确实需要。 - Randyaa

3
  • ulimit最大内存大小和虚拟内存设置为无限制?

ulimit在两者上都是无限制的。我应该在哪里检查虚拟内存限制? - Randyaa
使用带有 -a 开关的 ulimit 命令。ulimit -a - Jeshurun

3

我写了两个应用程序,一个中等大小,另一个相对较小。我启动中等大小的应用程序(在Linux上,CentOS),不带任何参数(Java服务器),它可以正常运行。但是当我启动较小的应用程序时,使用“Java客户端”,它会告诉我它无法保留足够的空间,并且无法运行。我进行了实验,并使用- Xms和- Xmx,都是10m,它们都可以正常运行... 想想看!


2

我将一台机器的内存从2GB升级到4GB,然后立即开始出现以下错误:

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

问题出在ulimit上,我把可寻址空间设置为1GB。将其增加到2GB后问题得以解决。
-Xms和-Xmx没有影响。
看起来Java试图按比例获取内存,如果无法获取则会失败。

2
您需要考虑升级您的操作系统和Java。Java 5.0已经停止支持,但如果您无法升级到Java 6,您可以使用最新的补丁级别22!
32位Windows系统仅限于约1.3 GB,所以将其最大值设置为1.8是很好的。注意:这是连续内存的问题,随着系统运行,内存空间可能会变得不连续,因此我并不感到惊讶您遇到了这个问题。
64位操作系统没有这个问题,因为它具有更多的虚拟空间,您甚至不需要升级到64位版本的Java来利用它。
顺便说一下,在我的经验中,32位Java 5.0可能比64位Java 5.0更快。直到多年后,Java 6更新10才对64位更快。

2
我最近遇到了这个问题。我有3个Java应用程序,堆大小为1024m或1280m。Java正在查看交换空间中的可用空间,如果没有足够的内存可用,则JVM会退出。
为了解决这个问题,我不得不结束一些分配了大量虚拟内存的程序。
我在x86-64 Linux上运行,使用64位的JVM。

2
这可能有点跑题,但有两件事情值得注意。以下两点都假定您正在运行32位的Linux系统。
1. Linux上有一个进程大小限制,在CentOS上记得大约是2.5GB,并且在内核中进行配置(即重新编译以更改)。一旦您将所有JVM代码+ Permgen空间和所有其他JVM库相加,您的进程可能会达到这个限制。
2. 第二个问题是我遇到过的,您可能已经用完了地址空间,听起来很奇怪。当我尝试使用1.5GB堆运行Glassfish时,当它尝试通过分叉javac编译JSP时,它会失败,因为操作系统无法为新创建的进程分配足够的地址空间,即使该机器上有12GB的内存。这里可能会发生类似的情况。
很抱歉,以上两个问题的唯一解决方法是升级到64位内核。
希望这对您有所帮助。

正如我之前提到的,如果SuSE和RedHat两个系统都有这个问题,我会同意你的观点。它们都是32位操作系统,但限制不同。Redhat似乎在2500+ MB左右,而SuSE系统似乎在1850 MB左右。 - Randyaa
不同的发行版可能对进程大小限制有不同的默认值。它们也有不同的内核版本。 - Matt K

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