简体   繁体   English

当20%的堆仍然是免费的时,为什么我会得到OutOfMemory?

[英]Why do I get OutOfMemory when 20% of the heap is still free?

I've set the max heap to 8 GB. 我已将最大堆设置为8 GB。 When my program starts using about 6.4 GB (as reported in VisualVM), the garbage collector starts taking up most of the CPU and the program crashes with OutOfMemory when making a ~100 MB allocation. 当我的程序开始使用大约6.4 GB(在VisualVM中报告)时,垃圾收集器开始占用大部分CPU,并且在进行~100 MB分配时程序与OutOfMemory崩溃。 I am using Oracle Java 1.7.0_21 on Windows. 我在Windows上使用Oracle Java 1.7.0_21。

My question is whether there are GC options that would help with this. 我的问题是,是否有GC选项可以帮助解决这个问题。 I'm not passing anything except -Xmx8g. 除了-Xmx8g,我没有传递任何东西。

My guess is the heap is getting fragmented, but shouldn't the GC compact it? 我的猜测是堆已经碎片化了,但GC应该不紧凑吗?

Collecting bits and pieces of information (which is surprisingly difficult, since the official documentation is quite bad), I've determined... 收集信息的部分(这非常困难,因为官方文档非常糟糕),我已经确定......

There are generally two reasons this may happen, both related to fragmentation of free space (ie, free space existing in small pieces such that a large object cannot be allocated). 这可能发生两种原因,两者都与自由空间的碎片有关(即,小块中存在的自由空间使得无法分配大对象)。 First, the garbage collector might not do compaction, which is to say it does not defragment the memory. 首先,垃圾收集器可能不会进行压缩,也就是说它不会对内存进行碎片整理。 Even a collector that does compaction may not do it perfectly well. 即使是压实的收集器也可能不能很好地完成。 Second, the garbage collector typically splits the memory area into regions that it reserves for different kinds of objects, and it may not think to take free memory from the region that has it to give to the region that needs it. 其次,垃圾收集器通常将内存区域拆分为它为不同类型的对象保留的区域,并且它可能不会考虑从具有它的区域获取空闲内存以提供给需要它的区域。

The CMS garbage collector does not do compaction, while the others (the serial, parallel, parallelold, and G1) do. CMS垃圾收集器不执行压缩,而其他(串行,并行,并行和G1)执行压缩。 The default collector in Java 8 is ParallelOld. Java 8中的默认收集器是ParallelOld。

All garbage collectors split memory into regions, and, AFAIK, all of them are too lazy to try very hard to prevent an OOM error. 所有的垃圾收集器都将内存分成了区域,而且,AFAIK,它们都太懒了,不能很努力地防止出现OOM错误。 The command line option -XX:+PrintGCDetails is very helpful for some of the collectors in showing the sizes of the regions and how much free space they have. 命令行选项-XX:+PrintGCDetails对于某些收集器显示区域的大小以及它们有多少可用空间非常有用。

It is possible to experiment with different garbage collectors and tuning options. 可以尝试不同的垃圾收集器和调整选项。 Regarding my question, the G1 collector (enabled with the JVM flag -XX:+UseG1GC ) solved the issue I was having. 关于我的问题, G1收集器(使用JVM标志-XX:+UseG1GC )解决了我遇到的问题。 However, this was basically down to chance (in other situations, it OOMs more quickly). 然而,这基本上是机会(在其他情况下,OOM更快)。 Some of the collectors (the serial, cms, and G1) have extensive tuning options for selecting the sizes of the various regions, to enable you to waste time in futilely trying to solve the problem. 一些收集器(串行,cms和G1)具有广泛的调整选项,用于选择不同区域的大小,使您可以徒劳地浪费时间尝试解决问题。

Ultimately, the real solutions are rather unpleasant. 最终,真正的解决方案是相当不愉快的。 First, is to install more RAM. 首先,是安装更多RAM。 Second, is to use smaller arrays. 第二,是使用较小的阵列。 Third, is to use ByteBuffer.allocateDirect . 第三,是使用ByteBuffer.allocateDirect Direct byte buffers (and their int/float/double wrappers) are array-like objects with array-like performance that are allocated on the OS's native heap. 直接字节缓冲区(及其int / float / double包装器)是类似于数组的对象,具有类似于数组的性能,这些对象在操作系统的本机堆上分配。 The OS heap uses the CPU's virtual memory hardware and is free from fragmentation issues and can even effectively use the disk's swap space (allowing you to allocate more memory than available RAM). 操作系统堆使用CPU的虚拟内存硬件,没有碎片问题,甚至可以有效地使用磁盘的交换空间(允许您分配比可用RAM更多的内存)。 A big drawback, however, is that the JVM doesn't really know when direct buffers should be deallocated, making this option more desirable for long-lived objects. 然而,一个很大的缺点是JVM并不真正知道何时应该释放直接缓冲区,这使得这个选项对于长期存在的对象更为理想。 The final, possibly best, and certainly most unpleasant option is to allocate and deallocate memory natively using JNI calls, and use it in Java by wrapping it in a ByteBuffer . 最后的,可能是最好的,当然也是最令人不愉快的选择是使用JNI调用本地分配和释放内存,并通过将其包装在ByteBuffer来在Java中使用它。

Which garbage collector are you using? 你在使用哪个垃圾收集器? CMS doesn't do any compaction. CMS不做任何压缩。 Try using the new G1 garbage collector - this does some compaction. 尝试使用新的G1垃圾收集器 - 这会做一些压缩。

For a bit of context: the G1 garbage collector, or `Garbage First' collector splits up the heap into chunks and after identifying (marking) all the garbage it will evacuate a chunk by copying all the live bits into a different chunk - this is what achieves compaction. 对于一些上下文:G1垃圾收集器或“垃圾优先”收集器将堆分成块并在识别(标记)所有垃圾后,它将通过将所有实时位复制到不同的块中来撤出块 - 这是是什么实现了压实。

To use include the option -XX:+UseG1GC 要使用包含选项-XX:+UseG1GC

This gives a great explanation of G1 and garbage collection in Java in general. 给出了Java中G1和垃圾收集的一般解释。

Whenever this problem has show up in the past, the actual free memory was much lower than it appeared. 每当这个问题在过去出现时,实际的可用内存远低于它出现的内存。 You can print the amount of free memory when an OutOfMemoryError occurs. 发生OutOfMemoryError时,可以打印可用内存量。

try {
    byte[] array = new byte[largeMemorySize];

} catch(OutOfMemroyError e) {
    System.out.printf("Failed to allocate %,d bytes, free memory= %,d%n", 
        largeMemorySize, Runtime.getRuntime().freeMemory());
    throw e;
}

Most likely, you are trying to allocate a large amount of contiguous memory, but all of the free memory is in little bits and pieces all over the place. 最有可能的是,你试图分配大量的连续内存,但是所有的空闲内存都是一点一点点。 Also, when the garbage collector starts taking up all of the processing time, that means that it is currently in the process of trying to find the maybe 1 or 2 objects in your whole set of objects that can be freed. 此外,当垃圾收集器开始占用所有处理时间时,这意味着它当前正在尝试在可以释放的整个对象集中找到可能的1或2个对象。 In this case, all I think you can do is work on breaking your objects down so that they do not need quite as much continuous memory (at least, not all at one time). 在这种情况下,我认为你可以做的就是打破你的对象,这样他们就不需要那么多的连续内存(至少,不是所有的一次)。

Edit: As far as I know, there is no way that you can get Java to pack the memory so that you can use that full 8 GB, as it would involve the Garbage Collector having to pause all of the other threads, moving their objects around, updating all of the references to those objects, then refreshing stale cache entries, and so on...a very, very expensive operation. 编辑:据我所知,你无法让Java打包内存,以便你可以使用那个完整的8 GB,因为它会让垃圾收集器不得不暂停所有其他线程,移动它们的对象周围,​​更新对这些对象的所有引用,然后刷新陈旧的缓存条目,等等...非常非常昂贵的操作。

See this about Memory Fragmentation 请参阅有关内存碎片的内容

-Xmx only ensures that the heap will not exceed 8GB in size but makes no guarantees that this much memory will actually be allocated. -Xmx只能确保堆大小不超过8GB,但不能保证实际分配这么多内存。 Does your machine only have 8GB of memory? 你的机器只有8GB的内存吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我得到堆outOfMemory异常? - Why do I get heap outOfMemory exception? 当Tomcat运行OutOfMemory时获取堆转储 - Get the heap dump when Tomcat runs OutOfMemory 为什么我得到 OutOfMemoryError 但堆转储显示很多 memory 是免费的 - Why do I get OutOfMemoryError but the heap dump shows a lot of memory as free 为什么这会产生堆OutOfMemory错误? - Why is this producing a heap OutOfMemory error? 为什么我在使用 @SuppressWarnings("unchecked") 时仍然收到警告? - Why do i still get warnings when using @SuppressWarnings("unchecked")? 多线程…堆空间时出现OutOfMemory异常? - OutOfMemory Exception when multithreading … heap space? Java Outofmemory堆空间错误:如何从向量中生成双端队列? - Java Outofmemory heap space error: How do I make a deque from a vector? 从Java 9降级为Java 8时,为什么仍会收到无效的源代码发布错误? - Why do I still get an invalid source release error when downgrading from Java 9 to Java 8? 使用 javascript 自动完成功能时,为什么在 Eclipse 中收到消息:“未处理的事件循环异常 Java 堆空间”? - Why do I get message: “Unhandled event loop exception Java heap space” in Eclipse when using javascript autocomplete? Java使用synchronized时我是否免费获得易失性功能? - Java When using synchronized do I get volatile functionality for free?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM