最近我在我的web应用程序中遇到了这个错误:
java.lang.OutOfMemoryError: PermGen space
这是一个典型的Hibernate/JPA + IceFaces/JSF应用程序,在Tomcat 6和JDK 1.6上运行。 显然,这可能会在多次重新部署应用程序后发生。
这是什么原因造成的?如何避免它?如何解决这个问题?
最近我在我的web应用程序中遇到了这个错误:
java.lang.OutOfMemoryError: PermGen space
这是一个典型的Hibernate/JPA + IceFaces/JSF应用程序,在Tomcat 6和JDK 1.6上运行。 显然,这可能会在多次重新部署应用程序后发生。
这是什么原因造成的?如何避免它?如何解决这个问题?
解决方案是在启动Tomcat时,在JVM命令行中添加这些标志:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
你可以通过关闭tomcat服务,进入Tomcat/bin目录并运行tomcat6w.exe来实现这一点。在“Java”选项卡下,将参数添加到“Java选项”框中。单击“确定”,然后重新启动服务。
如果出现错误“指定的服务不存在作为已安装的服务”,则应运行:
tomcat6w //ES//servicename
servicename 是在 services.msc 中查看的服务器名称。
来源:orx 在Eric's Agile Answers中的评论。
你最好尝试使用-XX:MaxPermSize=128M
而不是-XX:MaxPermGen=128M
。
我无法确定这个内存池的精确用途,但它与加载到JVM中的类的数量有关。(因此,启用Tomcat的类卸载可以解决问题。)如果您的应用程序在运行时生成和编译类,则更有可能需要比默认值更大的内存池。
应用服务器 PermGen 错误通常发生在多次部署后,这很可能是容器中对旧应用程序类加载器的引用所致。例如,使用自定义的日志级别类将导致由应用服务器的类加载器保持对其的引用。您可以使用现代(JDK6+)JVM 分析工具(如 jmap 和 jhat)检测这些跨类加载器的泄漏,查看哪些类继续保持在您的应用程序中,并重新设计或消除它们的使用。 常见嫌疑人包括数据库、记录器和其他基础框架级库。
请参阅 Classloader leaks: the dreaded "java.lang.OutOfMemoryError: PermGen space" exception,尤其是其 后续帖子。
人们常犯的一个错误是认为堆空间和永久代空间是一样的,这完全不正确。即使堆中还有很多空间可用,但仍然可能在永久代中耗尽内存。
导致 PermGen 内存溢出的常见原因是类加载器。每当一个类被加载到 JVM 中时,它的所有元数据以及 Classloader 都会保留在 PermGen 区域中,并且它们将在加载它们的 Classloader 准备进行垃圾回收时被回收。如果 Classloader 存在内存泄漏,则由它加载的所有类都将保留在内存中,并且在重复几次之后会导致 PermGen 内存不足。经典的例子是 Java.lang.OutOfMemoryError:PermGen Space in Tomcat。
现在有两种解决方法:
1. 找到内存泄漏的原因或确认是否存在内存泄漏。
2. 使用 JVM 参数 -XX:MaxPermSize
和 -XX:PermSize
来增加 PermGen 空间大小。
您还可以查看Java 中 Java.lang.OutOfMemoryError 的 2 个解决方案了解更多详情。
-XX:MaxPermSize
е’Ң-XX:PermSize
пјҹжҲ‘жүҫдёҚеҲ°catalina.bat
гҖӮжҲ‘зҡ„TomcatзүҲжң¬жҳҜ5.5.26
гҖӮ - Deckard如果使用Sun JVM,可以使用命令行参数-XX:MaxPermSize=128m
(将128替换为所需大小)。
-XX:MaxPermSize=256m
,如果仍然存在问题,请尝试使用-XX:MaxPermSize=512m
。XX:MaxPermSize=1024m
:) - igo-XX: MaxPermSize = 128m
(您可以尝试哪个效果最好)到 VM参数 中,因为我正在使用eclipse ide。在大多数JVM中,默认的PermSize约为 64MB ,如果项目中有太多的类或大量的字符串,它会耗尽内存。
对于eclipse,也在答案中进行了描述。
步骤1:在服务器选项卡上双击tomcat服务器
步骤2:打开启动配置文件并在现有的VM参数末尾添加-XX:MaxPermSize=128m
以增加内存空间。
我也遇到了一个类似的问题,当我部署和卸载复杂的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
空间消息表示内存中Permanent Generation区域已耗尽。
任何Java应用程序都被允许使用有限的内存。您特定应用程序可以使用的确切内存量在应用程序启动期间指定。
Java内存被分成不同的区域,如下图所示:
Metaspace:一个新的内存空间诞生了
JDK 8 HotSpot JVM现在使用本机内存来表示类元数据,并称为Metaspace;类似于Oracle JRockit和IBM JVM。
好消息是,这意味着不再有 java.lang.OutOfMemoryError: PermGen
空间问题,您也不需要使用Java_8_Download或更高版本来调整和监视此内存空间。
MetaSpace
也可能发生 OutOfMemory
。 - srk首先可以做的是增加永久代堆空间的大小。这无法使用通常的 -Xms(设置初始堆大小)和 -Xmx(设置最大堆大小)JVM参数来完成,因为正如提到的那样,永久代堆空间完全独立于常规的Java堆空间,而这些参数是用于设置常规Java堆空间的空间。然而,有类似的参数可以使用(至少在Sun/OpenJDK JVM中)来增加永久代堆的大小:
-XX:MaxPermSize=128m
默认为64m。
另一种永久解决方法是允许类被卸载,这样您的PermGen就不会耗尽:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
类似这样的东西过去对我产生了神奇的效果。不过,使用它们存在显著的性能折衷,因为 permgen 扫描将为您的每个请求进行额外的2次请求或类似行为。 您需要在使用中权衡好利弊。
您可以找到此错误的详细信息。
http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html