如何重现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
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将达到垃圾收集所花费的时间百分比超过阈值的点。