it-swarm.cn

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

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

Java.lang.OutOfMemoryError:PermGen空间

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

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

1206
Chris

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

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

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

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

Tomcat6w //ES//servicename

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

资料来源:orx评论 Eric的敏捷答案

560
Chris

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

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

250
toesterdahl

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

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

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

您还可以在Java中查看 2 Java的Java.lang.OutOfMemoryError 解决方案以获取更多详细信息。

67
Peter

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

40
user17163

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

36
bassist

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

对于Eclipse,它也在 answer 中描述。

步骤1 :双击 Servers Tab上的Tomcat服务器

enter image description here

步骤2 打开启动Conf 并将-XX: MaxPermSize = 128m添加到现有 VM arguements 的末尾。

enter image description here

26
prayagupd

在部署和取消部署复杂的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();
22
Edward Torbett

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

15
faisalbhagat

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

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

15
Jeremy

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

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

Java内存分为不同的区域,可以在下图中看到:

enter image description here

Metaspace:一个新的内存空间诞生了

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

好消息是它意味着没有Java.lang.OutOfMemoryError: PermGen空间问题,你不需要使用 Java_8_Download 或更高版本调整和监视这个内存空间。

14
Santosh Jadi

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

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

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

13
Edwin Buck

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

在Eclipse中转到 servers tab双击我的Tomcat 7.0中注册的服务器,它打开我的文件服务器一般注册信息。在 “常规信息”部分 单击链接 “打开启动配置” ,这将打开VM中参数选项卡中服务器选项的执行_最后添加的参数这些两个条目

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

准备好了。

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

    -XX:MaxPermSize=128m
    
  3. 将Initial Memory Pool设置为1024(可选)。
  4. 将Maximum Memory Pool设置为1024(可选)。
  5. 单击确定。
  6. 重新启动Tomcat服务。
8
Lucky

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

6
Darshan

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

5
Nikem

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

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

5
Yannis Sermetziadis

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

4
Tim Howland

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

4
Pankaj Shinde

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

希望这个有所帮助。

4
kiwilisk

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

4
sandeep

为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>  
3
Alejandro Pablo Tkachuk

他们说Tomcat的最新版本(6.0.28或6.0.29)处理重新部署servlets much better的任务。

3
Tony Ennis

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

-XX:+CMSClassUnloadingEnabled

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

-XX:+UseConcMarkSweepGC
3
sendon1982

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

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

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

3
Maze

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

你在做什么?

进行的交易量是多少?

你加载了多少数据?

等等.

等等.

等等

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

显然这可以在重新部署应用程序几次后发生

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

3
OscarRyz

如果你在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版本。

2
Hrishikesh Kumar

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

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

2
Daniel

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

每当我尝试运行mvn clean packagecommand时,它显示以下错误和 “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

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

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

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

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

2
NIKHIL CHAURASIA

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

2
Ross Peoples

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

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

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

1
dev

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

在Netbeans中:

转到服务选项卡 - >在服务器上右键 - >选择属性 - >转到平台选项卡 - >内部vm选项类型-Xms1024m

在我的情况下,我给了-Xms4096m

这是截图:

enter image description here

0
Satyadev