最近我在我的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的Agile Answers的评论.
你最好尝试-XX:MaxPermSize=128M
而不是-XX:MaxPermGen=128M
.
我无法准确地使用这个内存池,但它与加载到JVM中的类的数量有关.(因此为tomcat启用类卸载可以解决问题.)如果应用程序在运行时生成并编译类,则更可能需要大于默认值的内存池.
多个部署后发生的应用程序服务器PermGen错误很可能是由容器保存到旧应用程序的类加载器中的引用引起的.例如,使用自定义日志级别类将导致应用程序服务器的类加载器保留引用.您可以使用现代(JDK6 +)JVM分析工具(如jmap和jhat)来查看这些类间加载器泄漏,以查看应用程序中继续保留哪些类,以及重新设计或消除它们的使用.通常的嫌疑人是数据库,记录器和其他基础框架级库.
请参阅Classloader泄漏:可怕的"java.lang.OutOfMemoryError:PermGen space"异常,特别是其后续帖子.
人们常犯的错误是认为堆空间和permgen空间是相同的,这根本不是真的.您可能在堆中剩余大量空间但仍可能在permgen中耗尽内存.
PermGen中OutofMemory的常见原因是ClassLoader.每当将类加载到JVM中时,其所有元数据以及类加载器都保存在PermGen区域,并且当加载它们的类加载器准备好进行垃圾回收时,它们将被垃圾收集.在案例类中,类加载器有一个内存泄漏,它所加载的所有类都将保留在内存中,并且一旦重复几次就会导致permGen outofmemory.经典的例子是Java.lang.OutOfMemoryError:Tomcat中的PermGen空间.
现在有两种方法可以解决这个问题:
1.找出内存泄漏的原因或是否有任何内存泄漏.
2.使用JVM参数-XX:MaxPermSize
和增加PermGen Space的大小-XX:PermSize
.
您还可以在Java中查看2解决Java.lang.OutOfMemoryError的更多详细信息.
-XX:MaxPermSize=128m
对Sun JVM 使用命令行参数(显然将128替换为您需要的任何大小).
尝试-XX:MaxPermSize=256m
,如果它仍然存在,请尝试-XX:MaxPermSize=512m
我添加 -XX: MaxPermSize = 128m
(你可以尝试哪种方法效果最好)到VM Arguments,因为我正在使用eclipse ide.在大多数JVM中,默认的PermSize大约为64MB,如果项目中有太多的类或大量的字符串,则会耗尽内存.
对于日食,它也在答案中描述.
步骤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内存分为不同的区域,可以在下图中看到:
Metaspace:一个新的记忆空间诞生了
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
默认值为64米.
另一种妥善处理的方法是允许卸载类,这样你的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启动参数.
从Tomcat的bin目录中打开tomcat7w或在开始菜单中键入Monitor Tomcat(将打开一个带有各种服务信息的选项卡式窗口).
在Java Options文本区域中添加以下行:
-XX:MaxPermSize=128m
将Initial Memory Pool设置为1024(可选).
将Maximum Memory Pool设置为1024(可选).
单击确定.
重新启动Tomcat服务.
由于使用大空间而不是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,问题永远不会发生.
此外,如果您在webapp中使用log4j,请在log4j 文档中查看此段落.
似乎如果您正在使用PropertyConfigurator.configureAndWatch("log4j.properties")
,则在取消部署Web应用程序时会导致内存泄漏.
如果您有真正的内存泄漏,增加永久生成大小或调整GC参数将无济于事.如果您的应用程序或它使用的某个第三方库,泄漏类加载器,唯一真正和永久的解决方案是找到此泄漏并修复它.有许多工具可以帮助您,最近的一个是Plumbr,它刚刚发布了具有所需功能的新版本.