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

如何重现Java OutOfMemoryError-超出了GC开销限制,java,garbage-collection,out-of-memory,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++){

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

    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));
        }
    }
SecureRandom random=new SecureRandom();
对于(int i=0;i<100000;i++){
HashMap=newHashMap();
对于(int j=0;j<30;j++){
put(新的BigInteger(130,随机).toString(32),新的BigInteger(130,随机).toString(32));
}
}
我也提供了-XX:+UsegCoveredLimit jvm参数,但无法获取错误。是否有任何简单可靠的方法/黑客可以获得此错误?

此:

HashMap<String, String> map = new HashMap<String, String>();
HashMap map=newhashmap();
在循环中确定范围,并且在循环迭代时没有对创建的映射的外部(长期)引用。因此,在每个循环迭代结束时,每个映射都有资格进行垃圾收集


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

我认为这应该可以做到这一点。。。如果运行时间足够长:

HashMap<Long, String> map = new HashMap<Long, String>();
for (long i = 0; true; i++) {
    for (int j = 0; j < 100; j++) {
        String s = "" + j;
        map.put(i, s);
    }
}
HashMap map=newhashmap();
for(长i=0;真;i++){
对于(int j=0;j<100;j++){
字符串s=”“+j;
地图.put(i,s);
}
}

我所做的是慢慢积累非垃圾的数量,同时创造大量的垃圾。如果运行此操作直到非垃圾几乎填满了堆的所有部分,则GC将达到垃圾收集时间百分比超过阈值的程度。

由于您尚未接受任何答案,我将假设它们中没有一个对您有效。这里有一个可以。但首先,回顾一下:

如果在垃圾收集中花费的时间太多,并行收集器将抛出OutOfMemoryError:如果在垃圾收集中花费的时间超过总时间的98%,而堆的恢复时间不足2%

因此,您必须消耗几乎所有的堆,保持其已分配状态,然后分配大量垃圾。把很多东西放进
地图中
不会对你有好处

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;
        }
    }
}
为了完整起见,这里是我正在使用的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)

现在我想问你:你到底为什么要这样做?

将地图保存在另一个集合中,该集合在
for
循环之外声明,并在第一个
for
循环之前添加
while(true)
。问得好!复制“堆OOM”很容易,但复制开销限制要复杂得多。。。我想知道这是否可能!(有意地,即)将外部for循环设为while(true)这是复制错误。@StephenC-是的,100%为true。尽管这肯定会触发“堆OOM”,而不是开销限制,对吗?它仍然抛出java.lang.OutOfMemoryError:java堆空间增加内部循环的循环计数器。我想如果你想缩短运行时间,你总是可以
-Xmx8M
)不。。。如果您打算折磨JVM,那么应该尽可能延长痛苦的时间。;-)无法使用此代码获取高于20%的gc活动。我认为,与幸存者相比,它产生了太多的垃圾,这实际上对现代gc算法是有益的。此外,这里的老一代变化太少了。我认为,折磨JVM的一个好方法是这样一种方法。一旦达到某个堆占用率,它可能会增加一些删除,然后,如果已经配置了这样的限制,则应该可靠地产生“超出开销限制”。+1对于永久消耗几乎所有内存的非常好的方法,为了复制。回答你的问题:想测试JVM是否仍然能够以某种方式从这种状态恢复,可能需要一些主管或其他服务的帮助。老实说,我也不明白这个异常的意义,因为JVM在这个异常之后很快就没有响应了。我更希望JVM终止(fail fast approach)。@mkorszun-这才是真正的例外:关闭JVM,而不是继续尝试释放空间。与大多数OOM错误一样,它表明您没有分配足够的堆来运行程序(有时“足够”是无限的,但更常见的是程序有一个它需要的实际上限)。有一次,我在一个真实的程序中看到了这个错误,我们维护了一个由传入消息更新的大型对象集。这些信息是短暂的,因此会不断收集。当我们增加堆时,错误消失了。
> 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)