如何重现Java OutOfMemoryError – 超出了GC开销限制

我的方法是创建十万个本地集合并用随机字符串填充它们,如下所示:

SecureRandom random = new SecureRandom(); for(int i = 0 ; i < 100000 ; i++){ HashMap map = new HashMap(); for(int j = 0 ; j < 30 ; j++){ map.put(new BigInteger(130, random).toString(32), new BigInteger(130, random).toString(32)); } } 

我也提供了-XX:+ UseGCOverheadLimit jvm参数,但无法获取错误。 是否有任何简单可靠的方法/黑客来获取此错误?

既然你没有接受任何答案,我会假设他们都没有为你工作。 这是一个会。 但首先,审查触发此错误的条件 :

如果在垃圾收集中花费了太多时间,并行收集器将抛出OutOfMemoryError:如果超过98%的总时间花费在垃圾收集中,并且回收的堆少于2%

所以,你必须消耗几乎所有的堆,保持它的分配,然后分配大量的垃圾。 将大量内容放入Map并不会为您做到这一点。

 public static void main(String[] argv) throws Exception { List fixedData = consumeAvailableMemory(); while (true) { Object data = new byte[64 * 1024 - 1]; } } private static List consumeAvailableMemory() throws Exception { LinkedList holder = new LinkedList(); while (true) { try { holder.add(new byte[128 * 1024]); } catch (OutOfMemoryError ex) { holder.removeLast(); return holder; } } } 

consumeAvailableMemory()方法用相对较小的内存块填充堆。 “相对较小”很重要,因为JVM会将“大”对象(我的经验中为512k字节)直接放入终身代,让年轻一代空着。

在我消耗了大部分堆后,我只是分配并丢弃。 这个阶段较小的块大小很重要:我知道我将有足够的内存用于至少一次分配,但可能不超过两次。 这将使GC保持活动状态。

运行它会在一秒钟内产生所需的错误:

 > java -Xms1024m -Xmx1024m GCOverheadTrigger Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at GCOverheadTrigger.main(GCOverheadTrigger.java:12) 

而且,为了完整性,这是我正在使用的JVM:

 > java -version java version "1.6.0_45" Java(TM) SE Runtime Environment (build 1.6.0_45-b06) Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode) 

现在我的问题是你:为什么世界上你想要这样做?

这个:

 HashMap map = new HashMap(); 

循环中作用域,并且在循环迭代时没有创建对映射的外部(长期)引用。 因此,每个循环迭代结束时,每个映射都有资格进行垃圾收集。

您需要在循环外部创建一个对象集合,并使用循环来填充该集合。

我认为这应该可以解决问题…如果你运行得足够长:

 HashMap map = new HashMap(); for (long i = 0; true; i++) { for (int j = 0; j < 100; j++) { String s = "" + j; map.put(i, s); } } 

我正在做的是慢慢增加非垃圾量,同时创造大量垃圾。 如果运行此操作直到非垃圾填满几乎所有堆,则GC将达到垃圾收集所花费的时间百分比超过阈值的点。