处理“java.lang.OutOfMemoryError:PermGen space”错误

时间:2008-09-17 22:13:48

标签: exception memory-leaks out-of-memory java-6 permgen

最近我在我的网络应用程序中遇到了这个错误:

  

java.lang.OutOfMemoryError:PermGen space

这是在Tomcat 6和JDK 1.6上运行的典型Hibernate / JPA + IceFaces / JSF应用程序。 显然,重新部署应用程序几次后就会发生这种情况。

是什么导致它以及可以采取哪些措施来避免它? 我该如何解决这个问题?

32 个答案:

答案 0 :(得分:560)

解决方案是在启动Tomcat时将这些标志添加到JVM命令行:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

您可以通过关闭tomcat服务,然后进入Tomcat / bin目录并运行tomcat6w.exe来实现。在“Java”选项卡下,将参数添加到“Java选项”框中。单击“确定”,然后重新启动该服务。

如果您收到错误指定的服务不存在作为已安装的服务,您应该运行:

tomcat6w //ES//servicename

其中 servicename 是在services.msc中查看的服务器名称

来源:orx对Eric's Agile Answers的评论。

答案 1 :(得分:250)

您最好先尝试 -XX:MaxPermSize=128M 而不是-XX:MaxPermGen=128M

我无法确切知道这个内存池的确切用法,但它与加载到JVM中的类的数量有关。 (因此,为tomcat启用类卸载可以解决问题。)如果应用程序在运行时生成并编译类,则更有可能需要大于默认值的内存池。

答案 2 :(得分:152)

App server PermGen多次部署后发生的错误很可能是由容器对旧应用程序的类加载器的引用引起的。例如,使用自定义日志级别类将导致应用程序服务器的类加载器保留引用。您可以使用现代(JDK6 +)JVM分析工具(如jmap和jhat)来查看这些类间装入程序泄漏,以查看应用程序中继续保留哪些类,以及重新设计或消除它们的使用。通常的嫌疑人是数据库,记录器和其他基础框架级库。

请参阅Classloader leaks: the dreaded "java.lang.OutOfMemoryError: PermGen space" exception,尤其是followup post

答案 3 :(得分:67)

人们常犯的错误是认为堆空间和permgen空间是相同的,这根本不是真的。您可能在堆中剩余大量空间但仍可能在permgen中耗尽内存。

PermGen中OutofMemory的常见原因是ClassLoader。每当将类加载到JVM中时,其所有元数据以及类加载器都保存在PermGen区域,并且当加载它们的类加载器准备好进行垃圾回收时,它们将被垃圾收集。在案例类中,类加载器有一个内存泄漏,它所加载的所有类都将保留在内存中,并且一旦重复几次就会导致permGen outofmemory。经典的例子是Java.lang.OutOfMemoryError:PermGen Space in Tomcat

现在有两种方法可以解决这个问题:
1.找出内存泄漏的原因或是否有内存泄漏 2.使用JVM参数-XX:MaxPermSize-XX:PermSize增加PermGen Space的大小。

您还可以在Java中查看2 Solution of Java.lang.OutOfMemoryError以获取更多详细信息。

答案 4 :(得分:40)

对Sun JVM使用命令行参数-XX:MaxPermSize=128m(显然将128替换为您需要的任何大小)。

答案 5 :(得分:36)

尝试-XX:MaxPermSize=256m如果仍然存在,请尝试-XX:MaxPermSize=512m

答案 6 :(得分:26)

added -XX: MaxPermSize = 128m(你可以尝试哪种方法效果最好)到 VM Arguments ,因为我正在使用eclipse ide。在大多数JVM中,default PermSize大约是 64MB ,如果项目中有太多类或大量的字符串,则会耗尽内存。

对于eclipse,它也在answer描述。

第1步:双击服务器标签上的tomcat服务器

enter image description here

第2步打开启动会话并将-XX: MaxPermSize = 128m添加到现有 VM争论的末尾。

enter image description here

答案 7 :(得分:22)

在部署和取消部署复杂的Web应用程序时,我一直在反对这个问题,并且认为我会添加一个解释和我的解决方案。

当我在Apache Tomcat上部署应用程序时,会为该应用程序创建一个新的ClassLoader。然后使用ClassLoader加载所有应用程序的类,并且在取消部署时,一切都应该很好地消失。然而,实际上它并不那么简单。

在Web应用程序生命周期中创建的一个或多个类包含一个静态引用,该引用位于该行的某处,引用了ClassLoader。由于引用最初是静态的,因此没有任何垃圾收集会清除此引用 - ClassLoader及其加载的所有类都将保留。

经过几次重新部署后,我们遇到了OutOfMemoryError。

现在这已成为一个相当严重的问题。我可以确保在每次重新部署后重新启动Tomcat,但这会占用整个服务器,而不仅仅是重新部署的应用程序,这通常是不可行的。

因此,我在代码中整理了一个解决方案,该解决方案适用于Apache Tomcat 6.0。我没有在任何其他应用程序服务器上进行测试,并且必须强调如果没有在任何其他应用程序服务器上进行修改,很可能无法工作

我还想说,我个人讨厌这个代码,如果现有代码可以更改为使用正确的关闭和清理方法,没有人应该使用它作为“快速修复”。唯一应该使用的是如果你的代码所依赖的外部库(在我的情况下,它是一个RADIUS客户端),它不提供清理自己的静态引用的方法。

无论如何,关于代码。这应该在应用程序取消部署时调用 - 例如servlet的destroy方法或(更好的方法)ServletContextListener的contextDestroyed方法。

//Get a list of all classes loaded by the current webapp classloader
WebappClassLoader classLoader = (WebappClassLoader) getClass().getClassLoader();
Field classLoaderClassesField = null;
Class clazz = WebappClassLoader.class;
while (classLoaderClassesField == null && clazz != null) {
    try {
        classLoaderClassesField = clazz.getDeclaredField("classes");
    } catch (Exception exception) {
        //do nothing
    }
    clazz = clazz.getSuperclass();
}
classLoaderClassesField.setAccessible(true);

List classes = new ArrayList((Vector)classLoaderClassesField.get(classLoader));

for (Object o : classes) {
    Class c = (Class)o;
    //Make sure you identify only the packages that are holding references to the classloader.
    //Allowing this code to clear all static references will result in all sorts
    //of horrible things (like java segfaulting).
    if (c.getName().startsWith("com.whatever")) {
        //Kill any static references within all these classes.
        for (Field f : c.getDeclaredFields()) {
            if (Modifier.isStatic(f.getModifiers())
                    && !Modifier.isFinal(f.getModifiers())
                    && !f.getType().isPrimitive()) {
                try {
                    f.setAccessible(true);
                    f.set(null, null);
                } catch (Exception exception) {
                    //Log the exception
                }
            }
        }
    }
}

classes.clear();

答案 8 :(得分:15)

1)增加PermGen内存大小

首先可以做的是使永久代堆空间的大小更大。使用通常的-Xms(设置初始堆大小)和-Xmx(设置最大堆大小)JVM参数无法做到这一点,因为如上所述,永久生成堆空间完全独立于常规Java堆空间, 这些参数为这个常规Java堆空间设置了空间。但是,有类似的参数可以使用(至少使用Sun / OpenJDK jvms)来使永久代堆的大小更大:

 -XX:MaxPermSize=128m

默认为64米。

2)启用扫描

另一种妥善处理的方法是允许卸载类,以便PermGen永远不会用完:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

这样的东西在过去对我有用。但有一件事,在使用这些产品时会有显着的性能折衷,因为permgen扫描会对您提出的每个请求或类似的内容提出额外的2个请求。你需要平衡你的使用与权衡。

您可以找到此错误的详细信息。

http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html

答案 9 :(得分:15)

java.lang.OutOfMemoryError: PermGen空格消息表示内存中的Permanent Generation区域已耗尽。

允许任何Java应用程序使用有限的内存。在应用程序启动期间指定特定应用程序可以使用的确切内存量。

Java内存分为不同的区域,如下图所示:

enter image description here

元空间:一个新的记忆空间诞生

JDK 8 HotSpot JVM现在使用本机内存来表示类元数据,称为Metaspace;类似于Oracle JRockit和IBM JVM的。

好消息是,这意味着不再存在java.lang.OutOfMemoryError: PermGen空间问题,您无需再使用Java_8_Download或更高版本调整和监控此内存空间。

答案 10 :(得分:15)

或者,您可以切换到处理permgen而不是sun的jvm的JRockit。它通常也有更好的表现。

http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html

答案 11 :(得分:13)

这些天最简单的答案是使用Java 8.

它不再为PermGen空间专门保留内存,允许PermGen内存与常规内存池混合使用。

请记住,如果您不希望Java 8抱怨它们不执行任何操作,则必须删除所有非标准-XXPermGen...=... JVM启动参数。

答案 12 :(得分:13)

我遇到了我们在这里讨论的问题,我的场景是eclipse-helios + tomcat + jsf,你正在做的是将一个简单的应用程序部署到tomcat。我在这里展示了同样的问题,解决方法如下。

在eclipse中转到服务器选项卡双击我的案例tomcat 7.0中注册的服务器,它打开我的文件服务器常规注册信息。在“常规信息”部分单击链接“打开启动配置”,这将在最终添加的VM参数的Arguments选项卡中打开服务器选项的执行两个条目

-XX: MaxPermSize = 512m
-XX: PermSize = 512m

准备好了。

答案 13 :(得分:8)

  1. 从Tomcat的bin目录中打开tomcat7w,或在开始菜单中键入Monitor Tomcat (将打开一个带有各种服务信息的选项卡式窗口。)
  2. 在Java Options文本区域中添加以下行:

    -XX:MaxPermSize=128m
    
  3. 将初始内存池设置为1024(可选)。
  4. 将最大内存池设置为1024(可选)。
  5. 点击确定。
  6. 重新启动Tomcat服务。

答案 14 :(得分:6)

由于使用大空间而不是jvm提供空间来执行代码,因此发生了Perm gen space错误。在UNIX操作系统中解决此问题的最佳解决方案是更改bash文件上的某些配置。以下步骤解决了问题。

在终端上运行命令gedit .bashrc

使用以下值创建JAVA_OTPS变量:

export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"

保存bash文件。在终端上运行命令exec bash。重启服务器。

我希望这种方法可以解决您的问题。如果使用的Java版本低于8,则有时会出现此问题。但是如果你使用Java 8,问题永远不会发生。

答案 15 :(得分:5)

此外,如果您在webapp中使用log4j,请在log4j documentation中查看此段落。

似乎如果您使用PropertyConfigurator.configureAndWatch("log4j.properties"),则在取消部署Web应用程序时会导致内存泄漏。

答案 16 :(得分:5)

如果您有真正的内存泄漏,增加永久生成大小或调整GC参数将无济于事。如果您的应用程序或它使用的某个第三方库,泄漏类加载器,唯一真正和永久的解决方案是找到此泄漏并修复它。有许多工具可以帮助您,最近的一个工具是Plumbr,它刚刚发布了具有所需功能的新版本。

答案 17 :(得分:4)

我尝试了几个答案,最后完成这项工作的唯一事情是pom中编译器插件的这种配置:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
        <fork>true</fork>
        <meminitial>128m</meminitial>
        <maxmem>512m</maxmem>
        <source>1.6</source>
        <target>1.6</target>
        <!-- prevent PermGen space out of memory exception -->
        <!-- <argLine>-Xmx512m -XX:MaxPermSize=512m</argLine> -->
    </configuration>
</plugin>
希望这个有所帮助。

答案 18 :(得分:4)

设置-XX:PermSize=64m -XX:MaxPermSize=128m。稍后您也可以尝试增加MaxPermSize。希望它能奏效。同样适合我。仅设置MaxPermSize对我没有用。

答案 19 :(得分:4)

我有一个Hibernate + Eclipse RCP的组合,尝试使用-XX:MaxPermSize=512m-XX:PermSize=512m,它似乎对我有效。

答案 20 :(得分:4)

也为我解决了这个问题;但是,我注意到servlet的重启次数要差得多,所以虽然它在生产中更好,但它在开发过程中有点拖累。

答案 21 :(得分:3)

我遇到了完全相同的问题,但不幸的是,没有一个建议的解决方案对我有用。在部署期间没有发生这个问题,我也没有进行任何热部署。

在我的情况下,在执行我的Web应用程序时,每次都在同一点发生问题,同时将(通过休眠)连接到数据库。

This link(前面也提到过)确实提供了足够的内容来解决问题。将jdbc-(mysql)-driver移出WEB-INF并进入jre / lib / ext /文件夹似乎解决了这个问题。这不是理想的解决方案,因为升级到较新的JRE需要重新安装驱动程序。 另一个可能导致类似问题的候选者是log4j,所以你可能也想移动那个

答案 22 :(得分:3)

他们说Tomcat的最新版本(6.0.28或6.0.29)处理了更好地重新部署servlet 更多的任务。

答案 23 :(得分:3)

内存配置取决于应用的性质。

你在做什么?

进行的交易量是多少?

您要加载多少数据?

可能您可以分析您的应用并开始清理应用中的某些模块。

  

显然,重新部署应用程序几次后可能会发生这种情况

Tomcat有热部署,但它消耗内存。请尝试偶尔重新启动容器。您还需要知道在生产模式下运行所需的内存量,这似乎是该研究的好时机。

答案 24 :(得分:3)

在这种情况下,第一步是检查是否允许GC从PermGen卸载类。标准的JVM在这方面相当保守 - 类是为了永生而生。因此,一旦加载,即使没有代码使用它们,类也会保留在内存中。当应用程序动态创建大量类并且更长时间不需要生成的类时,这可能会成为一个问题。在这种情况下,允许JVM卸载类定义可能会有所帮助。这可以通过向启动脚本添加一个配置参数来实现:

torte

默认情况下,此参数设置为false,因此要启用此选项,您需要在Java选项中显式设置以下选项。如果启用CMSClassUnloadingEnabled,GC也将扫描PermGen并删除不再使用的类。请记住,只有在使用以下选项启用UseConcMarkSweepGC时,此选项才有效。因此,当运行ParallelGC或上帝禁止串行GC时,请确保通过指定以下内容将GC设置为CMS:

-XX:+CMSClassUnloadingEnabled

答案 25 :(得分:3)

为Tomcat分配更多内存不是正确的解决方案。

正确的解决方案是在销毁和重新创建上下文(热部署)后进行清理。解决方案是阻止内存泄漏。

如果您的Tomcat / Webapp服务器告诉您无法取消注册驱动程序(JDBC),请取消注册它们。这将阻止内存泄漏。

您可以创建ServletContextListener并在web.xml中对其进行配置。以下是ServletContextListener示例:

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.log4j.Logger;

import com.mysql.jdbc.AbandonedConnectionCleanupThread;

/**
 * 
 * @author alejandro.tkachuk / calculistik.com
 *
 */
public class AppContextListener implements ServletContextListener {

    private static final Logger logger = Logger.getLogger(AppContextListener.class);

    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        logger.info("AppContextListener started");
    }

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        logger.info("AppContextListener destroyed");

        // manually unregister the JDBC drivers
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                logger.info(String.format("Unregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                logger.info(String.format("Error unregistering driver %s", driver), e);
            }

        }

        // manually shutdown clean up threads
        try {
            AbandonedConnectionCleanupThread.shutdown();
            logger.info("Shutting down AbandonedConnectionCleanupThread");
        } catch (InterruptedException e) {
            logger.warn("SEVERE problem shutting down AbandonedConnectionCleanupThread: ", e);
            e.printStackTrace();
        }        
    }
}

在这里,您可以在web.xml中配置它:

<listener>
    <listener-class>
        com.calculistik.mediweb.context.AppContextListener 
    </listener-class>
</listener>  

答案 26 :(得分:2)

对我有用的唯一方法是使用JRockit JVM。我有MyEclipse 8.6。

JVM的堆存储正在运行的Java程序生成的所有对象。 Java使用new运算符创建对象,并在运行时在堆上分配新对象的内存。垃圾收集是自动释放程序不再引用的对象所包含的内存的机制。

答案 27 :(得分:2)

如果您在eclipse IDE中获得此功能,即使在设置参数后也是如此 --launcher.XXMaxPermSize-XX:MaxPermSize等,如果你得到同样的错误,很可能是eclipse正在使用一个错误版本的JRE,它本来是由某些第三方应用程序安装的并且设置为默认。这些有缺陷的版本不会获取PermSize参数,因此无论您设置什么,您仍然会收到这些内存错误。因此,在您的eclipse.ini中添加以下参数:

-vm <path to the right JRE directory>/<name of javaw executable>

还要确保将eclipse中首选项中的默认JRE设置为正确的java版本。

答案 28 :(得分:2)

“他们”是错误的,因为我正在运行6.0.29并且即使在设置了所有选项之后也遇到了同样的问题。正如Tim Howland所说,这些选择只会推迟不可避免的事情。它们允许我在遇到错误之前重新部署3次,而不是每次重新部署时都重新部署。

答案 29 :(得分:2)

我遇到了类似的问题。 我的是JDK 7 + Maven 3.0.2 + Struts 2.0 + Google GUICE依赖注入项目。

每当我尝试运行 mvn clean package 命令时,它显示以下错误并且“BUILD FAILURE”发生

  

org.apache.maven.surefire.util.SurefireReflectionException:java.lang.reflect.InvocationTargetException;嵌套异常是java.lang.reflect.InvocationTargetException:null   java.lang.reflect.InvocationTargetException   引起:java.lang.OutOfMemoryError:PermGen空间

我尝试了以上所有有用的提示和技巧,但不幸的是没有一个对我有用。 对我有用的内容将在下面逐步介绍:=&gt;

  1. 转到您的pom.xml
  2. 搜索<artifactId>maven-surefire-plugin</artifactId>
  3. 添加新的<configuration>元素,然后添加<argLine>子元素,其中传递-Xmx512m -XX:MaxPermSize=256m,如下所示=&gt;
  4. <强> <configuration> <argLine>-Xmx512m -XX:MaxPermSize=256m</argLine> </configuration>

    希望它有所帮助,快乐编程:)

答案 30 :(得分:1)

您还可以通过执行以下操作来解决此问题:

rm -rf <tomcat-dir>/work/* <tomcat-dir>/temp/*

清除工作 temp 目录会使Tomcat做一个干净的启动。

答案 31 :(得分:0)

如果有人在netbeans中遇到同样的错误,那么这就是我修复它的方法。

在Netbeans中:

转到服务标签 - &gt;在服务器上 - &gt;选择属性 - &gt;转到平台选项卡 - &gt;内部vm选项类型-Xms1024m

就我而言,我已经给了-Xms4096m

以下是截图:

enter image description here