4

这是功课,不会撒谎。我需要编写一个程序来生成“java.lang.OutOfMemoryError: PermGen space”错误。

由于我无法参加讲座,所以我昨天做了几个小时的研究,这就是我到目前为止所做的。

起初我创建了一个程序,但我经常遇到这个错误:

java.lang.OutOfMemoryError: GC overhead limit exceeded

好的,所以我做了更多的研究并了解到我没有收到 PermGen 错误,因为虽然我创建了对象(字符串对象)我没有再次使用它们,所以它们被认为是垃圾。所以我改变了我的代码并不断得到这个:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

所以这是我当时的代码:

import java.util.ArrayList;
import java.util.List;

public class Test {

public static void main(String[] args) {
    List<String> test = new ArrayList<String>();
    test.add(new String(""));

    for (;;) {
        for (int i = 0; i<test.size(); i++){
            test.add(test.get(i));
        }
    }
}
}

同样在 VM 参数下,我有“-XX:PermSize=2m”(我尝试了不同的值)。有人告诉我我的代码是错误的,因为它一遍又一遍地使用相同的字符串。所以我试图改变这一点,但我仍然没有成功。然后我找到了这段代码:(导致 java.lang.OutOfMemoryError: PermGen space error 的算法

Random rnd = new Random();
List<String> interned = new ArrayList<String>();
for (;;) {
    int length = rnd.nextInt(100);
    StringBuilder builder = new StringBuilder();
    String chars = "abcdefghijklmnopqrstuvwxyz";
    for ( int i = 0; i < length; i++ ) {
        builder.append(chars.charAt(rnd.nextInt(chars.length())));
    }
    interned.add(builder.toString().intern());
}

所以如果我理解正确,这应该给我 PermGen 错误?但我仍然得到java堆错误。

我也发现了这个:http: //javaeesupportpatterns.blogspot.com/2011/10/java-7-features-permgen-removal.html 如果我理解正确,那么在使用 java7 时可能 java 堆空间错误是我的错误应该得到?不再可能出现 PermGen 错误?好的,所以我尝试将编译器和项目版本更改为 java 6。但仍然出现 Java 堆空间错误。

我知道我没有参加讲座是我的错,但我可以使用一些帮助来了解我在这里做错了什么或者我错过了什么?

4

3 回答 3

6

首先,简单的一条线如何重现 perm gen 错误(只需递归调用 main):

public class PermGenError {
    public static void main(String[] args) {
        main(new String[] { (args[0] + args[0]).intern() });
    }
}

应该使用参数启动:

whatever -XX:PermSize=8M -XX:MaxPermSize=8M

其次,为什么你的例子没有产生 perm gen 错误?您对字符串实习生的使用是正确的。然而,在最新的 JMV 规范中,java 虚拟机可以将内部化的字符串从永久代移出到另一个。现实生活中的场景(发生在生产环境中:-))是你可以看到 perm gen 99% 满了,应用程序非常慢,jvm 没有抛出任何错误,因为它在几代之间不断地重新洗牌。

第三,对于任何 GC/内存问题,我读过并经常参考的最好的论文是使用 5.0 Java[tm] 虚拟机调整垃圾收集

编辑

问题的更新是:

示例工作正常,但当我指定大烫发大小时仍然不起作用 - 获取“Java堆空间”

更新答案是:

在指定较大的 perm gen 大小时,您需要注意两个方面:

  1. 所有对象都首先进入年轻一代。因此,如果您的 perm gen 大小大于第一代 Java 堆空间的大小,将首先被抛出。在这个特定示例中,我使用一个超过 perm gen 大小的大字符串来重现错误。如果要重现“PermGen 空间”,则还需要指定堆大小。例如:

    • -Xms2048m -Xmx2048m -XX:PermSize=512M -XX:MaxPermSize=512M - 将导致'PermGen space'(大堆,小perm gen)
    • -Xms512m -Xmx512m -XX:PermSize=2048M -XX:MaxPermSize=2048M - 将导致“Java 堆空间”(小堆,大 perm 生成)
  2. 第二个方面是堆被分成几代,堆本身的内存是碎片化的。因此,当您指定 512M 的堆大小时,您无法将半 GB 长的字符串放入内存中(因为字符串在内部实现为数组并且需要作为一个连续的块存储在内存中)。您可以容纳的最大可能大约是该大小的一半(这取决于内存碎片的严重程度)。

于 2012-10-14T12:20:53.313 回答
3

PermGen Space是内存区域,Java 存储类定义和内部数据(不是您的应用程序创建和使用的数据)。所以基本上你并不需要一个大的和/或聪明的应用程序。该博客页面简要介绍了 Java 内存的结构以及不同区域的职责。

从理论上讲,通过定义一个低 perm gen 空间(就像您已经对-XX:PermSize=2m. 然后,您将几个库添加到您的应用程序类路径(Apache commons,可能是 HTMLUnit 或类似的东西)。下载几个 jars,一旦应用程序启动并且类加载器尝试将 jars 的类定义存储在 PermGen 空间中,您就会收到此错误。

如果这不起作用,请尝试使用 Tomcat,部署一些库并通过 web 应用程序管理器一遍又一遍地重新加载应用程序。

如果您需要导致此问题的算法,您可以使用 JVM 参数调试正在运行的应用程序-XX:+PrintGCDetails。这将显示每次使用 GC 时使用的 PermGen 空间的大小。这可以帮助您了解您是否进入了正确的方向以及您填充了哪个内存区域。PermGen 空间也存储具有对方法的引用的数组,因此这也可以解决您的问题。

于 2012-10-14T12:01:44.727 回答
1

所以如果我理解正确,这应该给我 PermGen 错误?但我仍然得到java堆错误。

java.lang.OutOfMemoryError:超出 GC 开销限制

是的。正在发生的事情是您正在使用一种 JVM 机制,该机制旨在阻止可能耗尽内存的程序在该过程中花费过多时间。

请参阅超出 GC 开销限制

您可以使用 -XX:-UseGCOverheadLimit 开关禁用此功能。我希望您随后会收到 OOME 说您的永久内存不足。


如果你现在得到这个:

线程“主”java.lang.OutOfMemoryError 中的异常:Java 堆空间

那么问题是你的常规堆空间在你用完 permgen 空间之前就用完了:

  • 增加常规堆大小。
  • 确保您的应用程序保留对所有这些实习字符串的引用。(如果它们变得无法访问。它们将被垃圾收集,并且您不会用完 permgen 空间。)
于 2012-10-14T12:07:57.390 回答