英文:
JVM Out of Memory Causes
问题
问题基于Oracle Hotspot JDK8。
当应用程序遇到java.lang.OutOfMemory: Java heap space
异常时,我认为有两个可能的原因。
- 分配的JVM堆大小达到了
-Xmx
指定的大小,而GC系统无法释放出足够的空间。 - 分配的JVM堆没有达到
-Xmx
,但是没有足够的物理内存供JVM堆增长。假设-Xms
<-Xmx
。
我知道1
是JVM抛出java.lang.OutOfMemory: Java heap space
异常的原因之一。2
是一个合理的原因吗?
我在一些文章中找到了提到java.lang.OutOfMemoryError: native memory exhausted
,但它们都限于IBM的网站。这个异常是否仅限于IBM实现的JVM,还是它是JVM规范中的标准异常?
我根据@Eugene提供的代码进行了一些实验。正如@Holger指出的,结果在不同的环境中会有所不同。我在CentOS x64和Win7 x64上进行了测试,使用的是Hotspot JDK8 x64。
为了简单起见,已禁用了交换和虚拟内存。
我逐步增加了内存限制(-Xmx和-Xms)。
I. -Xmx < 可用逻辑内存
- 在CentOS和Windows上都显示OutOfMemoryError: Java heap space
II. 可用逻辑内存 < -Xmx < 最大物理内存
- CentOS:GC尝试多次进行Full GC,出现Allocation Failure,然后进程被系统杀死,留下一个消息Killed。
- Windows:GC尝试多次进行Full GC,出现Allocation Failure,然后抛出OutOfMemoryError: Java heap space
III. -Xmx > 最大物理内存
- CentOS:与II中相同
- Windows:与II中相同
IV. -Xms > 最大物理内存
- CentOS:JVM似乎无法启动。错误消息类似于:
> Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000e62a0000, 349569024, 0) failed; error='Cannot allocate memory' (errno=12)
- Windows:JVM无法启动。错误消息类似于:
> Error occurred during initialization of VM
Could not reserve enough space for object heap
因此,相同的JVM在不同的操作系统中表现不同。
-
在Windows上,操作系统不会终止JVM。当内存使用量超过限制时,JVM总是抛出OutOfMemoryError: Java heap space。
-
在Linux上,操作系统会在内存不足时终止进程。
-
在两种操作系统上,当可用内存不满足JVM的最小要求时,JVM无法启动。
英文:
Questions are based on Oracle Hotspot JDK8.
When applicaton come across java.lang.OutOfMemory: Java heap space
exception, I suppose, where are two possible reasons.
- Allocated JVM heap size reaches
-Xmx
specified size and GC system cann't squeeze out enough space. - Allocated JVM heap doesn't reach
-Xmx
, but there are not enough physical memory for JVM heap to grow. Suppose-Xms
<-Xmx
.
I know 1
is a reason for JVM to throw out java.lang.OutOfMemory: Java heap space
exception. Is 2
a reasonable one cause?
I find some articles mentioned java.lang.OutOfMemoryError: native memory exhausted
, but they are all limited to IBM website. Is this expetion limited to IBM implemented JVM or it is a standard expetion in JVM Specification?
I did some experiments with code supplied by @Eugene in answer. As @Holger noted the result varies in different environments. I tested in on both CentOS x64 and Win7 x64, with Hotspot JDK8 x64.
For simplicity swap and virtual memory are disabled.
I increase memory bound (-Xmx and -Xms) step by step.
I. -Xmx < available logic memory
- On both CentOS and Windows it shows OutOfMemoryError: Java heap space
II. available logic memory < -Xmx < max physical memory
- CentOS: The GC try to Full GC serveral times, with Allocation Failure, and process be killed by system, leaving a message Killed.
- Windows: The GC try to Full GC serveral times, with Allocation Failure, and throw out OutOfMemoryError: Java heap space
III. -Xmx > max physical memory
- CentOS: same as in II
- Windows: same as in II
IV. -Xms > max physical memory
- CentOS: JVM seems fail to start. Error message is like:
> Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000e62a0000, 349569024, 0) failed; error='Cannot allocate memory' (errno=12)
- Windows: JVM failed to start. Error message is like:
> Error occurred during initialization of VM
Could not reserve enough space for object heap
So, the same JVM behave differently in different OS.
-
On windows, the OS doesn't kill JVM. And JVM always throw out OutOfMemoryError: Java heap space when memory usage grow exceeds.
-
On Linux, the OS kill processes when there is not enough memory.
-
On both OS JVM failed to start when available memory doesn't satisfy JVM minimal requirement.
答案1
得分: 1
首先,有更多的原因可以导致垃圾回收出现“内存不足”的失败,正如您问题下的评论所解释的那样。
证明第二点很简单,只需创建一些总是在分配内存的代码:
public static void main(String[] args) {
test(1);
}
static void test(int x){
List<byte[]> list = new ArrayList<>();
while(x == 1){
byte [] b =new byte[1 * 1024 * 1024];
b[100] = 42;
list.add(b);
}
System.out.println(list.hashCode());
}
然后使用 -Xms1g -Xmx100g
运行此代码,在一个拥有少于 100g
内存的系统上运行。您可以启用 GC 日志(例如,我使用了 "-Xlog:heap*=debug" "-Xlog:gc*=debug"
在 Java 9 的标志中),查看 GC 如何努力应对这个不断的内存分配,最终失败。
英文:
First of all there are more reasons for a GC to fail with "out of memory" as the comment under your question explains.
Proving point number (2) is easy, just create some code that always allocates:
public static void main(String[] args) {
test(1);
}
static void test(int x){
List<byte[]> list = new ArrayList<>();
while(x == 1){
byte [] b =new byte[1 * 1024 * 1024];
b[100] = 42;
list.add(b);
}
System.out.println(list.hashCode());
}
And run this with -Xms1g -Xmx100g
, on a system that has less then 100g
of RAM. You can enable GC logs (I did with "-Xlog:heap*=debug" "-Xlog:gc*=debug"
in java-9 flags for example) and see how hard GC is trying to cope with this constant allocation, ultimately failing.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论