最近我在我的 Web 应用程序中遇到了这个错误:
java.lang.OutOfMemoryError: PermGen 空间
它是运行在 Tomcat 6 和 JDK 1.6 上的典型 Hibernate/JPA + IceFaces/JSF 应用程序。显然,这可能会在重新部署应用程序几次后发生。
是什么原因造成的,可以做些什么来避免它?我该如何解决这个问题?
最近我在我的 Web 应用程序中遇到了这个错误:
java.lang.OutOfMemoryError: PermGen 空间
它是运行在 Tomcat 6 和 JDK 1.6 上的典型 Hibernate/JPA + IceFaces/JSF 应用程序。显然,这可能会在重新部署应用程序几次后发生。
是什么原因造成的,可以做些什么来避免它?我该如何解决这个问题?
解决方案是在启动 Tomcat 时将这些标志添加到 JVM 命令行:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
您可以通过关闭 tomcat 服务,然后进入 Tomcat/bin 目录并运行 tomcat6w.exe 来做到这一点。在“Java”选项卡下,将参数添加到“Java 选项”框。单击“确定”,然后重新启动服务。
如果您收到错误,则指定的服务不作为已安装的服务存在,您应该运行:
tomcat6w //ES//servicename
其中servicename是在 services.msc 中查看的服务器名称
资料来源:orx 对Eric 的敏捷答案的评论。
你最好尝试-XX:MaxPermSize=128M
而不是-XX:MaxPermGen=128M
。
我无法说出这个内存池的确切用途,但它与加载到 JVM 中的类的数量有关。(因此为 tomcat 启用类卸载可以解决问题。)如果您的应用程序在运行时生成和编译类,则更有可能需要比默认值更大的内存池。
多次部署后发生的应用服务器 PermGen 错误很可能是由容器对旧应用的类加载器的引用引起的。例如,使用自定义日志级别类将导致应用服务器的类加载器持有引用。您可以使用现代 (JDK6+) JVM 分析工具(例如 jmap 和 jhat)来检测这些类加载器间泄漏,以查看哪些类继续保存在您的应用程序中,并重新设计或消除它们的使用。通常的嫌疑人是数据库、记录器和其他基本框架级别的库。
请参阅Classloader 泄漏:可怕的“java.lang.OutOfMemoryError: PermGen space”异常,尤其是其后续帖子。
人们常犯的错误是认为堆空间和永久空间是相同的,这根本不是真的。您可能在堆中剩余大量空间,但在 permgen 中仍然可能耗尽内存。
PermGen 中 OutofMemory 的常见原因是 ClassLoader。每当一个类被加载到 JVM 中时,它的所有元数据以及 Classloader 都保存在 PermGen 区域中,当加载它们的 Classloader 准备好进行垃圾收集时,它们将被垃圾收集。如果 Classloader 存在内存泄漏,那么它加载的所有类都将保留在内存中,并在您重复几次后导致 permGen 内存不足。经典的例子是Java.lang.OutOfMemoryError:PermGen Space in Tomcat。
现在有两种方法可以解决这个问题:
1.查找内存泄漏的原因或者是否有内存泄漏。2. 使用 JVM 参数和.
增加 PermGen Space 的大小。-XX:MaxPermSize
-XX:PermSize
您还可以查看Java中 Java.lang.OutOfMemoryError的 2 解决方案以获取更多详细信息。
使用 Sun JVM 的命令行参数-XX:MaxPermSize=128m
(显然将 128 替换为您需要的任何大小)。
尝试-XX:MaxPermSize=256m
,如果它仍然存在,请尝试-XX:MaxPermSize=512m
我在使用 eclipse ide时向VM Arguments添加了 -XX: MaxPermSize = 128m
(您可以试验哪个效果最好) 。在大多数 JVM 中,默认 PermSize约为64MB,如果项目中有太多类或大量字符串,则会耗尽内存。
对于 Eclipse,它也在answer中进行了描述。
第 1 步:双击服务器选项卡中的 tomcat 服务器
第 2步:打开启动 Conf并添加-XX: MaxPermSize = 128m
到现有VM 争论的末尾。
在部署和取消部署复杂的 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();
java.lang.OutOfMemoryError: PermGen
空间消息表明永久代在内存中的区域已用完。
任何 Java 应用程序都可以使用有限的内存量。您的特定应用程序可以使用的确切内存量是在应用程序启动期间指定的。
Java 内存被分成不同的区域,如下图所示:
元空间:一个新的内存空间诞生了
JDK 8 HotSpot JVM 现在使用本机内存来表示类元数据,称为 Metaspace;类似于 Oracle JRockit 和 IBM JVM。
好消息是,这意味着不再有java.lang.OutOfMemoryError: PermGen
空间问题,并且您不再需要使用Java_8_Download或更高版本来调整和监视此内存空间。
或者,您可以切换到 JRockit,它处理 permgen 的方式与 sun 的 jvm 不同。它通常也具有更好的性能。
http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html
可以做的第一件事是使永久代堆空间的大小更大。这不能使用通常的 –Xms(设置初始堆大小)和 –Xmx(设置最大堆大小)JVM 参数来完成,因为如上所述,永久代堆空间与常规 Java 堆空间完全分开,并且这些参数设置这个常规 Java 堆空间的空间。但是,可以使用类似的参数(至少对于 Sun/OpenJDK jvms)来使永久代堆的大小更大:
-XX:MaxPermSize=128m
默认为 64m。
另一种解决这个问题的方法是允许卸载类,这样你的 PermGen 就永远不会用完:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
过去,类似的东西对我来说很神奇。不过有一件事,在使用这些方面有一个显着的性能折衷,因为 permgen 扫描会为您提出的每个请求或类似的东西发出额外的 2 个请求。您需要在使用与权衡之间取得平衡。
您可以找到此错误的详细信息。
http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html
我遇到了我们在这里谈论的问题,我的场景是 eclipse-helios + tomcat + jsf,而您正在做的是将一个简单的应用程序部署到 tomcat。我在这里展示了同样的问题,解决如下。
在 eclipse 中转到服务器选项卡中,双击我的案例 tomcat 7.0 中的注册服务器,它会打开我的文件服务器一般注册信息。在“常规信息”部分单击“打开启动配置”链接,这将在最后添加这两个条目的 VM 参数中的“参数”选项卡中打开服务器选项的执行
-XX: MaxPermSize = 512m
-XX: PermSize = 512m
并准备好。
现在最简单的答案是使用 Java 8。
它不再专门为 PermGen 空间保留内存,允许 PermGen 内存与常规内存池混合。
-XXPermGen...=...
请记住,如果您不希望 Java 8 抱怨它们不执行任何操作,则必须删除所有非标准JVM 启动参数。
在 Java 选项文本区域中添加以下行:
-XX:MaxPermSize=128m
由于使用了大空间而不是 jvm 提供的空间来执行代码,因此发生 Perm gen space 错误。
在 UNIX 操作系统中,这个问题的最佳解决方案是更改 bash 文件的一些配置。以下步骤可解决问题。
gedit .bashrc
在终端上运行命令。
创建JAVA_OTPS
具有以下值的变量:
export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"
保存 bash 文件。在终端上运行命令 exec bash。重新启动服务器。
我希望这种方法可以解决您的问题。如果您使用低于 8 的 Java 版本,有时会出现此问题。但是,如果您使用 Java 8,则永远不会出现问题。
此外,如果您在 webapp 中使用 log4j,请查看 log4j文档中的此段落。
似乎如果您正在使用PropertyConfigurator.configureAndWatch("log4j.properties")
,当您取消部署您的 webapp 时会导致内存泄漏。
如果您有真正的内存泄漏,增加永久代大小或调整 GC 参数将无济于事。如果您的应用程序或它使用的某个 3rd 方库泄漏类加载器,唯一真正且永久的解决方案是找到此泄漏并修复它。有许多工具可以帮助您,最近的一个是Plumbr,它刚刚发布了具有所需功能的新版本。
jrockit也为我解决了这个问题;然而,我注意到 servlet 的重启时间要差得多,所以虽然它在生产中更好,但它在开发中有点拖累。
我有 Hibernate+Eclipse RCP 的组合,尝试使用-XX:MaxPermSize=512m
and -XX:PermSize=512m
,它似乎对我有用。
设置-XX:PermSize=64m -XX:MaxPermSize=128m
。以后你也可以尝试增加MaxPermSize
。希望它会工作。我也一样。仅设置MaxPermSize
对我不起作用。
我尝试了几个答案,最终唯一完成的工作是 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>
希望这个有帮助。
内存的配置取决于您的应用程序的性质。
你在做什么?
处理的交易量是多少?
您要加载多少数据?
等等
等等
ETC
也许您可以分析您的应用程序并开始从您的应用程序中清理一些模块。
显然,这可能在重新部署应用程序几次后发生
Tomcat 有热部署,但它消耗内存。尝试不时重启你的容器。您还需要知道在生产模式下运行所需的内存量,这似乎是进行该研究的好时机。
他们说 Tomcat 的最新版本(6.0.28 或 6.0.29)可以更好地处理重新部署 servlet 的任务。
我遇到了完全相同的问题,但不幸的是,没有一个建议的解决方案真正适合我。该问题在部署期间没有发生,我也没有进行任何热部署。
在我的情况下,在我的网络应用程序执行期间,每次都在同一时间发生问题,同时连接(通过休眠)到数据库。
这个链接(前面也提到过)确实提供了足够的内部来解决问题。将 jdbc-(mysql)-driver 从 WEB-INF 移到 jre/lib/ext/ 文件夹中似乎已经解决了这个问题。这不是理想的解决方案,因为升级到较新的 JRE 需要您重新安装驱动程序。可能导致类似问题的另一个候选者是 log4j,因此您可能也想移动它
在这种情况下,第一步是检查 GC 是否允许从 PermGen 中卸载类。标准 JVM 在这方面相当保守——类生来就是为了永远存在。所以一旦加载,即使没有代码再使用它们,类也会留在内存中。当应用程序动态创建大量类并且生成的类在较长时间内不需要时,这可能会成为一个问题。在这种情况下,允许 JVM 卸载类定义会很有帮助。这可以通过在启动脚本中添加一个配置参数来实现:
-XX:+CMSClassUnloadingEnabled
默认情况下,它设置为 false,因此要启用它,您需要在 Java 选项中显式设置以下选项。如果启用 CMSClassUnloadingEnabled,GC 也会清除 PermGen 并删除不再使用的类。请记住,此选项仅在使用以下选项启用 UseConcMarkSweepGC 时才有效。因此,当运行 ParallelGC 或者,上帝禁止,串行 GC 时,请确保您已通过指定以下方式将 GC 设置为 CMS:
-XX:+UseConcMarkSweepGC
为 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>
“他们”是错误的,因为我正在运行 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
运算符创建对象,新对象的内存在运行时在堆上分配。垃圾收集是自动释放程序不再引用的对象所包含的内存的机制。
我有类似的问题。我的是基于 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 Caused by: java.lang.OutOfMemoryError: PermGen space
我尝试了上述所有有用的提示和技巧,但不幸的是没有一个对我有用。下面逐步描述了对我有用的方法:=>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
元素,然后添加<argLine>
子元素,其中传递-Xmx512m -XX:MaxPermSize=256m
如下所示 =><configuration>
<argLine>-Xmx512m -XX:MaxPermSize=256m</argLine>
</configuration>
希望它有所帮助,快乐的编程:)
您还可以通过执行以下操作来解决此问题:
rm -rf <tomcat-dir>/work/* <tomcat-dir>/temp/*
清除工作目录和临时目录可使 Tomcat 干净启动。
在尝试调试 JBoss 应用程序时,谁在 IntelliJ 中遇到了同样的问题:我刚刚将此添加-XX: MaxPermSize = 128m
到运行/调试配置中的 VM 选项中。您可以将其增加到 256m 以更有效地保证工作。