首页 文章

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

提问于
浏览
1193

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

java.lang.OutOfMemoryError:PermGen空间

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

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

30 回答

  • 2

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

  • 2

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

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

  • 13

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

  • 3

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

  • 25

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

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

  • 4

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

    -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
    

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

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

    tomcat6w //ES//servicename
    

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

    资料来源:orx对Eric's Agile Answers的评论 .

  • 5

    你最好试试 -XX:MaxPermSize=128M 而不是 -XX:MaxPermGen=128M .

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

  • 2

    由于使用大空间而不是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,问题永远不会发生 .

  • 8

    人们常犯的错误是认为堆空间和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以获取更多详细信息 .

  • 13

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

  • 15

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

  • 6

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

    对于eclipse,它也在answer中描述 .

    STEP 1 :双击 Servers Tab上的tomcat服务器

    enter image description here

    STEP 2Open launch Conf 并将 -XX: MaxPermSize = 128m 添加到现有 VM arguements 的末尾 .

    enter image description here

  • 4

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

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

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

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

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

    所以我没有在任何其他应用程序服务器上测试过,而且必须强调 this is very likely not to work without modification on any other application server .

    我还想说,我个人讨厌这段代码,而且 nobody should be using this as a "quick fix" if the existing code can be changed to use proper shutdown and cleanup methods . 唯一应该使用的是's an external library your code is dependent on (In my case, it was a RADIUS client) that doesn' t提供清理自己的静态引用的方法 .

    无论如何,关于代码 . 这应该在应用程序取消部署时调用 - 例如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();
    
  • 2

    或者,您可以切换到JRockit,其处理permgen与sun的jvm不同 . 它通常也有更好的性能 .

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

  • 152

    1)增加PermGen内存大小

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

    -XX:MaxPermSize=128m
    

    默认值为64米 .

    2)启用清扫

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

    -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
    

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

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

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

  • 3

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

    在eclipse中转到 servers tab双击我的案例tomcat 7.0中的注册服务器,它打开我的文件服务器一般注册信息 . 在 "General Information" 部分单击链接 "Open launch configuration" ,这将在最后添加的两个条目中添加的VM参数的Arguments选项卡中打开服务器选项的执行

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

    准备好了 .

  • 12

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

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

    请记住,如果您不做任何事情,则必须删除所有非标准的 -XXPermGen...=... JVM启动参数 .

  • 3

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

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

    Java memory is separated into different regions which can be seen in the following image:

    enter image description here

    Metaspace: A new memory space is born

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

    The good news is that it means no more java.lang.OutOfMemoryError: PermGen space problems and no need for you to tune and monitor this memory space anymore using Java_8_Download or higher.

  • 3
    • 从Tomcat的bin目录中打开tomcat7w或在开始菜单中键入Monitor Tomcat(将打开一个带有各种服务信息的选项卡式窗口) .

    • 在Java Options文本区域中添加以下行:

    -XX:MaxPermSize=128m
    
    • 将初始内存池设置为1024(可选) .

    • 将最大内存池设置为1024(可选) .

    • 单击“确定” .

    • 重新启动Tomcat服务 .

  • 5

    我尝试了几个答案,唯一最终完成工作的是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>
    

    希望这个有所帮助 .

  • 22

    jrockit也为我解决了这个问题;但是,我注意到servlet的重启时间要差得多,所以虽然它在 生产环境 中更好,但它在开发中是一种拖累 .

  • 35

    内存的配置取决于您的应用程序的性质 .

    你在做什么?

    进行的交易量是多少?

    你加载了多少数据?

    等等

    等等

    等等

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

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

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

  • 249

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

  • 4

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

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

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

  • 66

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

    -XX:+CMSClassUnloadingEnabled
    

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

    -XX:+UseConcMarkSweepGC
    
  • 551

    为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>
    
  • 4

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

  • 3

    如果一些人在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版本 .

  • 15

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

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

  • 39

    我有类似的问题 . 我的是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 space

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

    • 转到您的pom.xml

    • 搜索 <artifactId>maven-surefire-plugin</artifactId>

    • 添加一个新的 <configuration> 元素,然后添加 <argLine> 子元素,其中传递 -Xmx512m -XX:MaxPermSize=256m ,如下所示=>

    <configuration> <argLine>-Xmx512m -XX:MaxPermSize=256m</argLine> </configuration>

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

相关问题