最近,我在我的web应用程序中遇到了这个错误:
java.lang.OutOfMemoryError:永久生成空间
它是一个典型的Hibernate/JPA + IceFaces/JSF应用程序,运行在Tomcat 6和JDK 1.6上。 显然,这可能发生在重新部署应用程序几次之后。
是什么原因导致的,如何避免呢? 我该如何解决这个问题?
最近,我在我的web应用程序中遇到了这个错误:
java.lang.OutOfMemoryError:永久生成空间
它是一个典型的Hibernate/JPA + IceFaces/JSF应用程序,运行在Tomcat 6和JDK 1.6上。 显然,这可能发生在重新部署应用程序几次之后。
是什么原因导致的,如何避免呢? 我该如何解决这个问题?
当前回答
outofmemoryerror: PermGen space消息表示内存中的永久生成区域已耗尽。
任何Java应用程序都允许使用有限的内存。特定应用程序可以使用的确切内存量是在应用程序启动时指定的。
Java内存被划分为不同的区域,如下图所示:
Metaspace:一个新的内存空间诞生
JDK 8 HotSpot JVM现在使用本机内存来表示类元数据,被称为Metaspace;类似于Oracle的JRockit和IBM的JVM。
好消息是,这意味着不再存在java.lang.OutOfMemoryError: PermGen空间问题,也不再需要使用Java_8_Download或更高版本来调优和监视这个内存空间。
其他回答
对于Sun JVM,使用命令行参数-XX:MaxPermSize=128m(显然是将128替换为您需要的任何大小)。
对我来说,唯一有效的方法就是JRockit JVM。我有MyEclipse 8.6。
JVM的堆存储运行中的Java程序生成的所有对象。Java使用new操作符创建对象,并且在运行时在堆上为新对象分配内存。垃圾收集是一种自动释放程序不再引用的对象所包含的内存的机制。
我有我们在这里谈论的问题,我的场景是eclipse-helios + tomcat + jsf,而您所做的是将一个简单的应用程序部署到tomcat。我在这里展示了同样的问题,解决方法如下。
在eclipse转到服务器选项卡双击注册服务器在我的情况下tomcat 7.0,它打开我的文件服务器一般注册信息。在“一般信息”部分单击“打开启动配置”链接,这将打开在虚拟机参数参数选项卡中添加的服务器选项的执行,最后添加这两个条目
-XX: MaxPermSize = 512m
-XX: PermSize = 512m
和准备好了。
在部署和取消部署一个复杂的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();
另外,如果你在你的webapp中使用log4j,检查log4j文档中的这段。
似乎如果你正在使用propertyconfiguration . configureandwatch ("log4j.properties"),当你取消部署你的web应用程序时,你会导致内存泄漏。