处理“java.lang.OutOfMemoryError:PermGen空间”错误
最近我遇到了我的Web应用程序中的这个错误:
java.lang.OutOfMemoryError:PermGen空间
这是在Tomcat 6和JDK 1.6上运行的典型的Hibernate / JPA + IceFaces / JSF应用程序。 显然这可能会在重新部署应用程序几次之后发生。
是什么原因造成的,为避免这种情况可以采取什么措施 我如何解决这个问题?
解决scheme是在Tomcat启动时将这些标志添加到JVM命令行:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
你可以通过closurestomcat服务,然后进入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)来检测这些类间加载器泄漏,以查看应用程序中继续保留哪些类,然后重新devise或消除它们的使用。 常见的嫌疑犯是数据库,logging器和其他基本框架级库。
请参阅Classloader泄漏:令人畏惧的“java.lang.OutOfMemoryError:PermGen空间”exception ,特别是其后续文章 。
人们犯的常见错误是认为堆空间和空间空间是相同的,这是不正确的。 你可以在堆中留下很多空间,但是仍然可能会在内存中耗尽内存。
PermGen中OutofMemory的常见原因是ClassLoader。 每当一个类加载到JVM中时,它的所有元数据以及Classloader都保存在PermGen区域中,并且当加载它们的Classloader准备好进行垃圾回收时,它们将被垃圾回收。 如果Classloader存在内存泄漏,则所有加载的类都将保留在内存中,并且一旦重复了几次,就会导致内存溢出。 经典的例子是Java.lang.OutOfMemoryError:Tomcat中的PermGen空间 。
现在有两种方法可以解决这个问题:
1.找出内存泄漏的原因,或者是否有内存泄漏。
2.使用JVM param -XX:MaxPermSize
和-XX:PermSize
增加PermGen空间的大小。
您还可以在Java中检查Java.lang.OutOfMemoryError的2解决scheme以获取更多详细信息。
对于Sun JVM,使用命令行参数-XX:MaxPermSize=128m
(显然,将128replace为需要的大小)。
尝试-XX:MaxPermSize=256m
,如果它仍然存在,请尝试-XX:MaxPermSize=512m
我添加了 -XX: MaxPermSize = 128m
(你可以-XX: MaxPermSize = 128m
哪个效果最好)到VM参数,因为我使用的是Eclipse IDE。 在大多数JVM中, 默认的PermSize大约是64MB ,如果项目中有太多的类或者数量巨大的string,就会导致内存不足。
对于日食,这也是在回答中描述的。
步骤1 :双击服务器选项卡上的tomcat服务器
第2 步 : 打开启动Conf ,并将-XX: MaxPermSize = 128m
添加到现有虚拟机参数的末尾 。
在部署和取消部署复杂的Web应用程序时,我一直在对付这个问题,并认为我会添加一个解释和我的解决scheme。
当我在Apache Tomcat上部署应用程序时,会为该应用程序创build一个新的ClassLoader。 ClassLoader然后用来加载所有的应用程序的类,并在取消部署,一切都应该很好地消失。 但是,实际上并不那么简单。
在Web应用程序生命期间创build的一个或多个类拥有一个静态引用,在该行的某处引用ClassLoader。 由于引用最初是静态的,因此不会有大量的垃圾收集器清理这个引用 – ClassLoader和它所加载的所有类都在这里。
经过几次重新部署,我们遇到了OutOfMemoryError。
现在这已经成为一个相当严重的问题。 我可以确保在每次重新部署之后重新启动Tomcat,但是会取消整个服务器,而不是仅重新部署应用程序,这通常是不可行的。
所以相反,我已经把代码解决scheme放到了Apache Tomcat 6.0上。 我还没有在任何其他应用程序服务器上进行testing,并且必须强调, 如果不在其他任何应用程序服务器上进行修改 , 这很可能不起作用
我也想说,我个人讨厌这个代码, 如果现有的代码可以改变使用适当的closures和清理方法 , 没有人应该使用这个“快速修复” 。 唯一应该使用的是如果你的代码依赖于外部库(在我的情况下,它是一个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();
或者,您可以切换到JRockit,它处理permgen不同于sun的jvm。 它通常也有更好的performance。
http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html
我遇到了我们在这里讨论的问题,我的场景是eclipse-helios + tomcat + jsf,你正在做的是将一个简单的应用程序部署到tomcat。 我在这里显示了同样的问题,解决它如下。
在eclipse中去服务器选项卡双击注册的服务器在我的情况下tomcat 7.0,它打开我的文件服务器常规注册信息。 在“常规信息”部分,单击“打开启动configuration”链接,这将在最后添加的VM参数的参数选项卡中打开服务器选项的执行这两个条目
-XX: MaxPermSize = 512m -XX: PermSize = 512m
并准备好了。
现在最简单的答案就是使用Java 8。
它不再专门为PermGen空间预留内存,允许PermGen内存与常规内存池混合。
请记住,如果不希望Java 8抱怨他们没有做任何事情,则必须删除所有非标准的-XXPermGen...=...
JVM启动参数。
1)增加PermGen内存大小
我们能做的第一件事就是让永久代的堆空间变得更大。 这不能用通常的-Xms(设置初始堆大小)和-Xmx(设置最大堆大小)JVM参数来完成,因为如上所述,永久生成堆空间与常规Java堆空间完全分离,并且这些参数集这个常规Java堆空间的空间。 然而,也有类似的参数(至less在Sun / OpenJDK jvms中)可以使永久生成堆的大小更大:
-XX:MaxPermSize=128m
默认是64米。
2)启用扫描
另一种好的方法就是让class级卸载,这样你的PermGen永远不会用完:
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
这样的东西对我来说是过去的魔术。 但有一件事情,在使用这些产品时会有一个显着的性能折衷,因为permgen的扫描会使得每个请求的额外的2个请求或者这些线索的东西。 你需要平衡你的使用和权衡。
你可以find这个错误的细节。
http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html
java.lang.OutOfMemoryError: PermGen
空间消息指示存储器中永久代的区域已耗尽。
任何Java应用程序都被允许使用有限的内存。 应用程序启动期间指定了特定应用程序可以使用的确切内存量。
Java内存分为不同的区域,可以在下图中看到:
Metaspace:一个新的内存空间诞生了
JDK 8 HotSpot JVM现在使用本地内存来表示类的元数据,并被称为Metaspace; 类似于Oracle JRockit和IBM JVM的。
好消息是,这意味着没有更多的java.lang.OutOfMemoryError: PermGen
空间问题,并且不需要使用Download Java 8来调整和监视这个内存空间。
- 从Tomcat的bin目录中打开tomcat7w,或者在开始菜单中键入Monitor Tomcat(打开带有各种服务信息的选项卡式窗口)。
-
在Java选项文本区域附加这一行:
-XX:MaxPermSize=128m
- 将初始内存池设置为1024(可选)。
- 将最大内存池设置为1024(可选)。
- 点击确定。
- 重新启动Tomcat服务。
由于使用较大的空间,而不是由jvm提供空间来执行代码,所以发生了perm gen space错误。 在UNIX操作系统中,这个问题的最佳解决scheme是更改bash文件中的某些configuration。 以下步骤解决问题。
在terminal上运行命令gedit .bashrc
。
用以下值创buildJAVA_OTPS
variables:
export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"
保存bash文件。 在terminal上运行命令exec bash。 重新启动服务器。
我希望这种方法能解决你的问题。 如果使用低于8的Java版本,则有时会出现此问题。 但是如果使用Java 8,问题就不会发生。
如果你有一个真正的内存泄漏,增加永久代的大小或调整GC参数将无济于事。 如果你的应用程序或者它使用的某个第三方库泄露了类加载器,那么唯一真正的和永久的解决scheme就是find这个漏洞并修复它。 有很多工具可以帮助你,最近的一个就是Plumbr ,它刚刚发布了具有所需function的新版本。
另外,如果您在web应用程序中使用log4j,请在log4j 文档中查看本段落。
看起来,如果您使用PropertyConfigurator.configureAndWatch("log4j.properties")
,则会在卸载Web应用程序时导致内存泄漏。
我有一个Hibernate + Eclipse RCP的组合,尝试使用-XX:MaxPermSize=512m
和-XX:PermSize=512m
,它似乎为我工作。
设置-XX:PermSize=64m -XX:MaxPermSize=128m
。 稍后,您也可以尝试增加MaxPermSize
。 希望它能工作。 对我来说也是一样的 只设置MaxPermSize
没有为我工作。
我尝试了几个答案,唯一最终做的工作是编译器插件在pom中的这个configuration:
<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>
希望这一个帮助。
jrockit也为我解决了这个问题。 不过,我注意到servlet的重启时间要差得多,所以虽然在生产环境中比较好,但是却是一种拖延。
内存的configuration取决于您的应用程序的性质。
你在做什么?
交易金额是多less?
你加载了多less数据?
等等
等等
等等
也许你可以分析你的应用程序,并开始清理你的应用程序中的一些模块。
显然这可能会在重新部署应用程序几次之后发生
Tomcat具有热部署但消耗内存。 尝试一会儿重新启动您的容器。 你也需要知道在生产模式下运行所需的内存量,这对于这项研究来说似乎是一个好时机。
他们说,Tomcat(6.0.28或6.0.29)的最新版本可以更好地处理重新部署servlet的任务。
我遇到了完全相同的问题,但不幸的是没有build议的解决scheme真的为我工作。 这个问题在部署过程中没有发生,我也没有做任何热部署。
在我的情况下,每次在执行我的Web应用程序的同时,在连接(通过hibernate)到数据库时,都会出现这个问题。
这个链接 (也是前面提到的)确实提供了足够的内部来解决这个问题。 将jdbc-(mysql)-driver移出WEB-INF并移入jre / lib / ext /文件夹似乎解决了这个问题。 这不是理想的解决scheme,因为升级到更新的JRE将要求您重新安装驱动程序。 另一个可能导致类似问题的候选人是log4j,所以你可能也想移动那个
分配Tomcat更多的内存不是正确的解决scheme。
正确的解决scheme是在上下文被销毁并重新创build(热部署)之后进行清理。 解决办法是停止内存泄漏。
如果您的Tomcat / Webapp服务器告诉您注销驱动程序(JDBC)失败,则取消注册它们。 这将停止内存泄漏。
您可以创build一个ServletContextListener并在您的web.xml中进行configuration。 这里是一个示例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中configuration它:
<listener> <listener-class> com.calculistik.mediweb.context.AppContextListener </listener-class> </listener>
“他们”是错误的,因为我正在运行6.0.29,即使设置所有选项后也有同样的问题。 正如蒂姆·豪兰德(Tim Howland)所说,这些select只是推翻了必然。 他们让我重新部署3次,而不是每次重新部署错误。
如果你在eclipse IDE中得到这个,甚至在设置参数--launcher.XXMaxPermSize
, -XX:MaxPermSize
等之后,如果你得到相同的错误,最有可能的是eclipse使用的是bug版本的JRE,它将被某些第三方应用程序安装并设置为默认值。 这些越野车版本不拿起PermSize参数,所以不pipe你设置什么,你仍然不断收到这些内存错误。 所以,在你的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
运算符来创build对象,并且在运行时在堆上分配新对象的内存。 垃圾收集是自动释放不再由程序引用的对象所包含的内存的机制。
我有类似的问题。 我的是JDK 7 + Maven 3.0.2 + Struts 2.0 + Google GUICEdependency injection项目。
每当我尝试运行mvn clean package
命令,它显示以下错误和“BUILD FAILURE”发生
org.apache.maven.surefire.util.SurefireReflectionException:java.lang.reflect.InvocationTargetException; 嵌套exception是java.lang.reflect.InvocationTargetException:null java.lang.reflect.InvocationTargetException引起:java.lang.OutOfMemoryError:PermGen空间
我尝试了所有上述有用的提示和技巧,但不幸的是没有为我工作。 以下是一步一步描述我的工作:=>
- 去你的pom.xml
- search
<artifactId>maven-surefire-plugin</artifactId>
- 添加一个新的
<configuration>
元素,然后添加<argLine>
子元素,其中传递-Xmx512m -XX:MaxPermSize=256m
,如下所示=>
<configuration> <argLine>-Xmx512m -XX:MaxPermSize=256m</argLine> </configuration>
希望它有帮助,开心编程:)
这种情况下的第一步是检查GC是否允许从PermGen卸载课程。 标准的JVM在这方面比较保守 – class级是永生的。 所以一旦加载,即使没有代码使用它们,类仍然保留在内存中。 当应用程序dynamic创build大量类并且生成的类不再需要更长时间时,这可能会成为问题。 在这种情况下,允许JVM卸载类定义可能会有帮助。 这可以通过向启动脚本添加一个configuration参数来实现:
-XX:+CMSClassUnloadingEnabled
默认情况下,它被设置为false,所以为了启用它,你需要在Java选项中显式地设置下面的选项。 如果启用CMSClassUnloadingEnabled,则GC将扫描PermGen并删除不再使用的类。 请记住,只有使用下面的选项启用UseConcMarkSweepGC时,该选项才会起作用。 所以在运行ParallelGC或者上帝禁止使用串口GC的时候,请确保你已经把你的GC设置为CMS了:
-XX:+UseConcMarkSweepGC