无法创建PermGen错误

时间:2012-10-14 11:49:05

标签: java memory memory-management permgen

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

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

起初我创建了一个程序,我不断得到这个错误:

java.lang.OutOfMemoryError: GC overhead limit exceeded

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

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”(我尝试了不同的值)。 我被告知我的代码是错误的,因为它反复使用相同的字符串。所以我试图改变它,但我仍然没有成功。 然后我找到了这段代码:(Algorithms that lead to 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堆空间错误。

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

3 个答案:

答案 0 :(得分: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 /内存问题的最佳论文是Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine

编辑

更新问题是:

  

示例工作正常,但在我指定大烫发时仍然无法正常工作   size - 获取'Java堆空间'

更新回答是:

在指定更大的perm gen size时,需要注意两个:

  1. 所有物品首先进入年轻一代。因此,如果您的perm gen size大于第一代Java堆空间的大小将首先抛出。在这个特定的例子中,我使用了一个超过perm gen size的大字符串,以便重现错误。如果要重现'PermGen space',则还需要指定堆大小。例如:

    • -Xms2048m -Xmx2048m -XX:PermSize = 512M -XX:MaxPermSize = 512M - 将导致'PermGen space'(大堆,小烫发)
    • -Xms512m -Xmx512m -XX:PermSize = 2048M -XX:MaxPermSize = 2048M - 将导致'Java堆空间'(小堆,大烫发)
  2. 第二个方面是堆被分为几代,堆本身的内存被分段。因此,当您指定512M的堆大小时,您无法将半千兆字节长的字符串放入内存中(因为字符串内部实现为数组,需要作为一个连续的块存储在内存中)。你可以适应的最大值大概是这个大小的一半(这取决于内存碎片的严重程度)。

答案 1 :(得分:3)

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

理论上,通过定义低perm gen空间(正如您对-XX:PermSize=2m所做的那样),您可以很快得到异常。然后在应用程序类路径中添加几个库(Apache commons,也许是HTMLUnit或类似的东西)。下载几个罐子,一旦应用程序启动并且类加载器试图将你的罐子的类定义存储在PermGen空间中,你就会收到这个错误。

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

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

答案 2 :(得分:1)

  

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

     

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

烨。发生的事情是你正在使用一种JVM机制来阻止可能耗尽内存的程序在这个过程中花费太多时间。

请参阅GC overhead limit exceeded

您可以使用-XX:-UseGCOverheadLimit开关禁用此功能。我希望你会得到OOME,说你已经没有了permgen记忆。


如果你现在得到这个:

  

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

然后问题是你的常规堆在用完permgen空间之前空间不足了:

  • 增加常规堆大小。
  • 确保您的应用程序保留对所有这些内部字符串的引用。 (如果它们变得无法到达。它们将被垃圾收集,你将不会耗尽permgen空间。)