Java 内存不足,尽管我给了它足够的内存!
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2054074/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java runs out of memory, even though I give it plenty!
提问by spitzanator
So, I'm running a java server (specifically Winstone: http://winstone.sourceforge.net/)
所以,我正在运行一个 Java 服务器(特别是 Winstone:http: //winstone.sourceforge.net/)
Like this: java -server -Xmx12288M -jar /usr/share/java/winstone-0.9.10.jar --useSavedSessions=false --webappsDir=/var/servlets --commonLibFolder=/usr/share/java
像这样: java -server -Xmx12288M -jar /usr/share/java/winstone-0.9.10.jar --useSavedSessions=false --webappsDir=/var/servlets --commonLibFolder=/usr/share/java
This has worked fine in the past, but now it needs to load a bunch more stuff into memory than it has before.
这在过去工作得很好,但现在它需要比以前加载更多的东西到内存中。
The odd part is that, according to 'top', it has 15.0g of VIRT(ual memory) and it's RES(ident set) is 8.4g. Once it hits 8.4g, the CPU hangs at 100% (even though it's loading from disk), and eventually, I get Java's OutOfMemoryError. Presumably, the CPU hanging at 100% is Java doing garbage collection.
奇怪的是,根据'top',它有15.0g的VIRT(ual memory),它的RES(ident set)是8.4g。一旦达到 8.4g,CPU 就会挂起 100%(即使它是从磁盘加载),最终,我得到 Java 的 OutOfMemoryError。据推测,100% 挂起的 CPU 是 Java 进行垃圾收集。
So, my question is, what gives? I gave it 12 gigs of memory! And it's only using 8.2 gigs before it throws in the towel. What am I doing wrong?
所以,我的问题是,什么给出了?我给了它 12 演出的内存!而且它只使用了 8.2 个演出,然后就被淘汰了。我究竟做错了什么?
Oh, and I'm using java version "1.6.0_07" Java(TM) SE Runtime Environment (build 1.6.0_07-b06) Java HotSpot(TM) 64-Bit Server VM (build 10.0-b23, mixed mode)
哦,我正在使用 Java 版本“1.6.0_07”Java(TM) SE 运行时环境(构建 1.6.0_07-b06)Java HotSpot(TM) 64 位服务器 VM(构建 10.0-b23,混合模式)
on Linux.
在 Linux 上。
Thanks, Matt
谢谢,马特
回答by Stephen C
The odd part is that, according to 'top', it has 15.0g of VIRT(ual memory) and it's RES(ident set) is 8.4g. Once it hits 8.4g, the CPU hangs at 100% (even though it's loading from disk), and eventually, I get Java's OutOfMemoryError.
奇怪的是,根据'top',它有15.0g的VIRT(ual memory),它的RES(ident set)是8.4g。一旦达到 8.4g,CPU 就会挂起 100%(即使它是从磁盘加载),最终,我得到 Java 的 OutOfMemoryError。
I think you are misinterpreting things. The "-Xmx12288M" option does not reserve physical memory. Rather it sets an upper limit on the size of the Java heap. Java also needs memory for non-heap objects; e.g. permgen space, code space, memory mapped files, etcetera. It is quite plausible for a 12g heap + the non-heap memory used/shared by the JVM to add up to 15g.
我认为你在误解事情。“-Xmx12288M”选项不保留物理内存。相反,它设置了 Java 堆大小的上限。Java 也需要非堆对象的内存;例如永久代空间、代码空间、内存映射文件等。12g 堆 + JVM 使用/共享的非堆内存加起来很可能是 15g。
The 8.4g reported by top as RES is the amount of physical memory that is currently being used to run the JVM. It is not directly related to the size of your Java heap. Indeed, you would expect the RES number to move up and down as different processes' pages are swapped in and out by the OS virtual memory system. This is entirely outside the control of the JVM.
top 报告为 RES 的 8.4g 是当前用于运行 JVM 的物理内存量。它与 Java 堆的大小没有直接关系。实际上,您会期望 RES 编号随着操作系统虚拟内存系统换入和换出不同进程的页面而上下移动。这完全在 JVM 的控制之外。
Presumably, the CPU hanging at 100% is Java doing garbage collection.
据推测,100% 挂起的 CPU 是 Java 进行垃圾收集。
Yes. That's typically what happens.
是的。这就是通常发生的事情。
I can think of three possible explanations:
我能想到三种可能的解释:
Most likely, the operating system is unable to give your JVM the memory it is asking for because there is not enough swap disk space. For example if you have 2 processes with 15g of virtual memory each, that's 30gb. Given that you have 24g of physical memory, you will need at least 8g (probably more) of swap space. If the amount of physical memory allocatable to user processes + the amount of swap space is less than the total virtual space used by processes the OS will start refusing requests by the JVM to expand the heap. You can run "swapon -s" to see how much swap space is available / in use.
Your application may reallybe using all of the 12g of heap that you've said it can use, and that is not enough. (Maybe you've got a storage leak. Maybe it really needs more memory.)
It is also possible (but highly unlikely) that someone has set a process limit. You can use the shell builtin 'ulimit' command to see if this has been done; refer to "man ulimit" for details.
最有可能的是,操作系统无法为您的 JVM 提供它所要求的内存,因为没有足够的交换磁盘空间。例如,如果您有 2 个进程,每个进程有 15g 的虚拟内存,那就是 30gb。鉴于您有 24g 的物理内存,您将需要至少 8g(可能更多)的交换空间。如果可分配给用户进程的物理内存量 + 交换空间量小于进程使用的总虚拟空间量,操作系统将开始拒绝 JVM 扩展堆的请求。您可以运行“swapon -s”来查看有多少交换空间可用/正在使用。
您的应用程序可能真的使用了您说过可以使用的所有 12g 堆,但这还不够。(也许你有一个存储泄漏。也许它真的需要更多的内存。)
也有可能(但极不可能)有人设置了进程限制。您可以使用 shell 内置的 'ulimit' 命令来查看是否已完成;有关详细信息,请参阅“man ulimit”。
EDIT
编辑
If you use the
-verbose:gcand-XX:+PrintGCDetailsoptions, the GC might give you more clues as to what is happening. In particular, it will tell you how big the Java heap really is when you run out of memory.You could write a little Java app that simply allocates and doesn't free lots of memory, and see how much it manages to allocate before falling over with an OOM error when run with the same options as you are currently using. (I don't think this will tell you anything new. EDIT 2actually, it willtell you something if you run it in the way that @Dan suggests! )
If (as seems likely) the real problem is that you don't have enough swap space, there is nothing you can do on the Java side to fix this. You need to reconfigure your system to have more swap space. Refer to your Linux system administration documentation, the
manpages forswapon,mkswapetcetera.
如果您使用
-verbose:gc和-XX:+PrintGCDetails选项,GC 可能会为您提供有关正在发生的事情的更多线索。特别是,当内存不足时,它会告诉您 Java 堆的实际大小。您可以编写一个简单的 Java 应用程序,它只分配而不释放大量内存,并在使用与您当前使用的选项相同的选项运行时查看它在因 OOM 错误而失败之前设法分配了多少内存。(我认为这不会告诉您任何新信息。 实际上,编辑 2,如果您按照@Dan 建议的方式运行它,它会告诉您一些信息!)
如果(看起来很可能)真正的问题是您没有足够的交换空间,则在 Java 方面您无能为力来解决此问题。您需要重新配置系统以获得更多交换空间。请参阅您的Linux系统管理文档,该
man换页swapon,mkswap等等。
回答by gojomo
Sometimes OutOfMemorError doesn't mean object heap is used up -- but something else. Was there any other info on the error stack?
有时 OutOfMemorError 并不意味着对象堆已用完——而是其他原因。错误堆栈上还有其他信息吗?
In particular, the JVM needs a bunch of memory/address-space separate from the heap space. At some point, giving the process more object heap can leave less space for this other pool -- paradoxically making OOMEs more likely with larger '-Xmx' settings!
特别是,JVM 需要一堆与堆空间分开的内存/地址空间。在某些时候,给进程更多的对象堆可以为另一个池留下更少的空间——自相矛盾的是,使用更大的“-Xmx”设置更容易出现 OOME!
Memory-mapped files can eat a lot of address space; failing to close files properly can leave this space allocated until GC/finalization, which happens at an unpredictable time. Also, having a larger heap puts off GC/finalization -- meaning this native address space may stay reserved longer, so again: larger heaps can mean more frequent OOMEs due to depleted other memory.
内存映射文件会占用大量地址空间;未能正确关闭文件可能会将此空间分配到 GC/finalization,这发生在不可预测的时间。此外,拥有更大的堆会推迟 GC/终结——这意味着这个本机地址空间可能会保留更长的时间,所以再次重申:由于其他内存耗尽,更大的堆可能意味着更频繁的 OOME。
You can also use '-Xms' of the same value to force all heap address space to be grabbed immediately at startup -- perhaps triggering the failure at a more convenient/understandable/reproducible stage.
您还可以使用相同值的“-Xms”强制在启动时立即获取所有堆地址空间——也许会在更方便/可理解/可重现的阶段触发故障。
Finally, there's also a threshold at which an OOME is thrown even if no memory request has failed -- but GC is taking "too much" time, indicating garbage being created as fast as it's being collected, probably in a tight loop. A big inefficient load-from-disk where lots of garbage is created for a small amount of retained data might cause this. Look up [GC overhead limit] for details.
最后,还有一个阈值,即使没有内存请求失败,OOME 也会被抛出——但是 GC 花费了“太多”的时间,表明垃圾的创建速度与收集的速度一样快,可能是在一个紧密的循环中。从磁盘加载效率低下,其中为少量保留数据创建了大量垃圾可能会导致这种情况。查看[GC开销限制]了解详细信息。
回答by Arne Deutsch
The odd part is that, according to 'top', it has 15.0g of VIRT(ual memory) and it's RES(ident set) is 8.4g. Once it hits 8.4g, the CPU hangs at 100% (even though it's loading from disk), and eventually, I get Java's OutOfMemoryError. Presumably, the CPU hanging at 100% is Java doing garbage collection.
奇怪的是,根据'top',它有15.0g的VIRT(ual memory),它的RES(ident set)是8.4g。一旦达到 8.4g,CPU 就会挂起 100%(即使它是从磁盘加载),最终,我得到 Java 的 OutOfMemoryError。据推测,100% 挂起的 CPU 是 Java 进行垃圾收集。
I would guess that java has to use that much memory that it decides (or is forced) to use disk space for your data. Now the next time it has to collect garbage it has to use IO to do this. Doing garbage collection on disk is rather expensive for sure. And because you have that much objects the garbage collector may jump in again and again. This may be why your CPU is at 100% ... doing garbage collection for ever.
我猜想 java 必须使用它决定(或被迫)为您的数据使用磁盘空间的那么多内存。现在,下次它必须收集垃圾时,它必须使用 IO 来执行此操作。在磁盘上进行垃圾收集肯定是相当昂贵的。并且因为您有那么多对象,垃圾收集器可能会一次又一次地跳进来。这可能就是你的 CPU 处于 100% 的原因......永远做垃圾收集。
As Stephen has said -verbose:gcand -XX:+PrintGCDetailswill give more hints.
正如斯蒂芬所说-verbose:gc和-XX:+PrintGCDetails将提供更多提示。
But apart from this, maybe it will pay of to invest in an implementation that has not to load all the files at once? Working at the limit of your memory doesn't look like a winning strategy.
但除此之外,也许投资于不必一次加载所有文件的实现是值得的?在你的记忆力极限下工作看起来不像是一个成功的策略。
回答by jamesbtate
Can you upgrade to the latest version of java (1.6.0_17)?
你能升级到最新版本的java(1.6.0_17)吗?
回答by Dan Breslau
Maybe a dumb question, but are you sure that your swap partition has at least 15GB free when the program starts?
也许是一个愚蠢的问题,但你确定你的交换分区在程序启动时至少有 15GB 可用空间吗?
回答by Sab
And just to confirm, it ain't the PermGen space running out right?
只是为了确认,这不是永久代空间用完了吗?

