处理"java.lang.OutOfMemoryError: PermGen space"错误

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

java.lang.OutOfMemoryError:永久生成空间

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

是什么原因导致的,可以做些什么来避免它?如何解决这个问题?

1058193 次浏览

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

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

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

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

tomcat6w //ES//servicename

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

来源:orx对Eric的敏捷答案的评论。

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

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

也为我解决了这个问题;然而,我注意到servlet重启时间要糟糕得多,所以虽然它在生产中更好,但在开发中有点拖累。

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

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

类加载器泄漏:可怕的“java.lang.OutOfMemoryError:PermGen空间”异常,特别是它的后续帖子

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

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

你在干什么?

之前的交易金额是多少?

你加载了多少数据?

也许您可以分析您的应用程序并开始清理应用程序中的一些模块。

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

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

你最好尝试#0而不是-XX:MaxPermGen=128M

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

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

设置-XX:PermSize=64m -XX:MaxPermSize=128m。稍后你也可以尝试增加MaxPermSize。希望它会起作用。同样的对我有用。只设置MaxPermSize对我不起作用。

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

“他们”是错误的,因为我运行的是6.0.29,即使设置了所有的选项,也有同样的问题。正如蒂姆·豪兰上面所说,这些选项只会推迟不可避免的事情。它们允许我在遇到错误之前重新部署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版本。

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

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

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

在eclipse中转到服务器选项卡双击注册服务器在我的情况下tomcat 7.0,它打开我的文件服务器常规注册信息。在第"一般信息"节单击链接“开启启动配置”上,这将打开在参数选项卡中执行服务器选项在VM参数中添加这两个条目

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

和准备。

我遇到了完全相同的问题,但不幸的是,建议的解决方案都不适合我。问题没有在部署期间发生,我也没有做任何热部署。

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

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

另外,如果您在Web应用程序中使用log4j,请在log4j留档中检查此段落。

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

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

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

在Web应用程序的生命周期中创建的一个或多个类拥有一个静态引用,该引用在某处引用了ClassLoader。由于引用最初是静态的,任何垃圾收集都不会清理这个引用——ClassLoader和它加载的所有类都在这里。

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

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

因此,我用代码编写了一个解决方案,它适用于Apache Tomcat 6.0。我没有在任何其他应用程序服务器上进行测试,必须强调这很可能无法在任何其他应用程序服务器上进行修改

我还想说,就我个人而言,我讨厌这段代码和如果可以更改现有代码以使用正确的关闭和清理方法,则没有人应该将其用作“快速修复”。唯一应该使用它的时候是,如果你的代码依赖于一个外部库(在我的例子中,它是一个RADIUS客户端),它不提供清理自己静态引用的方法。

无论如何,继续代码。这应该在应用程序取消部署时调用——例如servlet的销毁方法或(更好的方法)ServletContextListener的contextDestro的方法。

//Get a list of all classes loaded by the current webapp classloaderWebappClassLoader 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();

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

希望这个能有所帮助。

人们常犯的错误是认为堆空间和永久空间是相同的,这根本不是真的。您可以在堆中剩余大量空间,但仍然可以在永久中运行内存溢出。

PermGen内存泄漏的常见原因是ClassLoader。每当一个类加载到JVM中时,它的所有元数据,连同Classloader,都保留在PermGen区域,当加载它们的Classloader准备好垃圾回收机制时,它们将被垃圾收集。如果Classloader有内存泄漏,那么它加载的所有类都将保留在内存中,一旦你重复几次,就会导致permGen内存泄漏。经典的例子是Java.lang.OutOfMemoryError:Tomcat中的PermGen空间

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

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

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

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

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

清除工作临时目录使Tomcat进行干净启动。

当我使用eclipse ide时,我从添加-XX: MaxPermSize = 128m(你可以实验哪个效果最好)到VM参数。在大多数JVM中,默认参数大小大约在64MB左右,如果项目中有太多的类或大量的字符串,它会运行内存溢出。

对于eclipse,它也在回答中描述。

STEP 1:双击服务器选项卡处的tomcat服务器

输入图片描述

STEP 2打开启动配置文件,并将-XX: MaxPermSize = 128m添加到现有VM论证的末尾。

输入图片描述

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

    -XX:MaxPermSize=128m
  3. Set Initial Memory Pool to 1024 (optional).
  4. Set Maximum Memory Pool to 1024 (optional).
  5. Click Ok.
  6. Restart the Tomcat service.

现在最简单的答案是使用Java8。

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

请记住,如果您不希望Java8抱怨它们什么都不做,则必须删除所有非标准的-XXPermGen...=... JVM启动参数。

1)增加PermGen内存大小

可以做的第一件事是使永久代堆空间的大小更大。这不能用通常的-Xms(设置初始堆大小)和-Xmx(设置最大堆大小)JVM参数来完成,因为如前所述,永久代堆空间与常规Java堆空间完全分开,这些参数设置了这个常规Java堆空间的空间。然而,也有类似的参数可以使用(至少在Sun/OpenJDK jvms中)来使永久代堆的大小更大:

 -XX:MaxPermSize=128m

缺省值64m。

2)启用扫描

另一种永远照顾它的方法是允许类被卸载,这样你的PermGen就永远不会用完:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

像这样的东西在过去对我来说很神奇。不过,有一件事,使用它们需要很大的性能权衡,因为永久扫描会为你发出的每个请求或类似的东西额外发出2个请求。你需要平衡你的使用和权衡。

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

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

Perm gen空间错误发生是由于使用了大空间而不是jvm提供的空间来执行代码。

在UNIX操作系统中解决此问题的最佳解决方案是更改bash文件上的某些配置。以下步骤解决了问题。

在终端上运行命令gedit .bashrc

使用以下值创建JAVA_OTPS变量:

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

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

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

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

每当我尝试运行#0命令时,它会显示以下错误并发生“建筑失败”

org.apache.maven.surefire.util.java.lang.reflect.InvocationTargetException;嵌套异常java.lang.reflect.InvocationTargetException: nulljava.lang.reflect.InvocationTargetException造成的原因:java.lang.OutOfMemoryError: PermGen space

我尝试了以上所有有用的提示和技巧,但不幸的是,没有一个对我有用。下面一步一步地描述了我的工作:=>

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

<配置>-Xmx512m-XX:256m

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

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

-XX:+CMSClassUnloadingEnabled

默认情况下,this设置为false,因此要启用它,您需要在Java选项中显式设置以下选项。如果启用CMSClassUnloadingEn的,GC也会扫描PermGen并删除不再使用的类。请记住,只有当使用以下选项启用UseConcMarkSweepGC时,此选项才会起作用。因此,在运行ParallelGC或Serial GC时,请确保您已通过指定将GC设置为CMS:

-XX:+UseConcMarkSweepGC

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

任何Java应用程序都可以使用有限的内存量。您的特定应用程序可以使用的确切内存量是在应用程序启动时指定的。

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

在此处输入图片描述

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

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

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

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

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

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

您可以创建一个ServletContextListener并在web.xml.中配置它。

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);
@Overridepublic void contextInitialized(ServletContextEvent arg0) {logger.info("AppContextListener started");}
@Overridepublic void contextDestroyed(ServletContextEvent arg0) {logger.info("AppContextListener destroyed");
// manually unregister the JDBC driversEnumeration<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 threadstry {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>

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

在NetBeans中:

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

在我的情况下,我已经给出了-Xms4096m

下面是截图:

在此处输入图片描述

在尝试调试JBoss应用程序时,谁在IntelliJ中遇到了同样的问题:我刚刚将此-XX: MaxPermSize = 128m添加到运行/调试COnfigurations中的VM选项。您可以将其增加到256m以更保证它会正常工作。

增加Tomcat内存

C:\Program Files\Apache Software Foundation\Tomcat 9.0\bin

或任何您使用tomcat的地方。并运行tomcat9w或您使用的任何版本。

之后跟着图片走

输入图片描述

将128更改为1024,并根据需要将其最大更改为1024或更多。