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

mko*_*zun 16 java garbage-collection out-of-memory

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

    SecureRandom random = new SecureRandom();
    for(int i = 0 ; i < 100000 ; i++){
        HashMap<String, String> map = new HashMap<String, String>();
        for(int j = 0 ; j < 30 ; j++){
            map.put(new BigInteger(130, random).toString(32), new BigInteger(130, random).toString(32));
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

kdg*_*ory 10

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

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

因此,您必须消耗几乎所有的堆,保持分配,然后分配大量垃圾.把很多东西放进Map去是不会为你做的.

public static void main(String[] argv)
throws Exception
{
    List<Object> fixedData = consumeAvailableMemory();
    while (true)
    {
        Object data = new byte[64 * 1024 - 1];
    }
}


private static List<Object> consumeAvailableMemory()
throws Exception
{
    LinkedList<Object> holder = new LinkedList<Object>();
    while (true)
    {
        try
        {
            holder.add(new byte[128 * 1024]);
        }
        catch (OutOfMemoryError ex)
        {
            holder.removeLast();
            return holder;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

而且,为了完整性,这里是我正在使用的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)
Run Code Online (Sandbox Code Playgroud)

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