27

我遇到了这个错误,CPU 使用率达到极限并且 JBoss 需要重新启动(java.lang.OutOfMemoryError: PermGen space)。

我找到了旧 JBoss 版本的解决方案来增加MaxPermSize. 我想JBoss7也是如此。

为了不再面临任何问题,哪个值足够好?有没有办法永久摆脱这个问题(比如让我们说使用像 JRockit 这样的不同 VM)?

4

4 回答 4

56

由于这发生在多次重新部署之后,听起来您遇到了类加载器泄漏,这是一种常见的permgen 泄漏

这些可爱的野兽的发生是因为从容器拥有的对象到作为从应用程序类加载器加载的类的实例的对象的正常(非弱)引用。如果这些引用在取消部署时没有被清除,那么应用程序的类加载器仍然有一个强大的引用链,所以它不能被 GC'd 并且加载的类不能被释放。

一个常见原因是容器类中的静态集合添加了对应用程序类的非静态引用。

JBoss AS 7 在其模块系统中有一些相当强大的规定来防止类加载器泄漏,所以我很惊讶你设法触发了一个。自从我从 Glassfish 迁移到 AS7 后,我还没有看到类加载器泄漏。

增加 MaxPermSize 会为您争取一些时间,但不会解决问题。

您确实需要弄清楚类加载器泄漏的原因。这样做很“有趣”。您享受税收、间歇性故障和清洁淋浴,对吗?请参阅第一部分中的链接,了解一些可以帮助您开始跟踪泄漏的博客。基本上,您需要使用VisualVMOQL来挖掘对应用程序类加载器的引用,或者进行堆转储并使用jhat(JDK 的一部分)来查找引用。无论哪种方式,我们的想法都是通过应用程序类的实例找出从应用程序服务器到类加载器的强引用链在哪里。

或者,它可以帮助获取您的应用程序的副本,然后开始从中提取部分内容,直到泄漏消失。您可以通过将 VisualVM 或其他监控连接到应用服务器 VM 并观察 PermGen 在两个或多个部署/取消部署周期后是否增加来判断它是否泄漏。考虑自动化部署/取消部署周期。将泄漏的原因缩小到应用程序的一小部分和/或其依赖项之一,并生成一个小的、独立的测试用例,然后将其作为 (a) JBoss AS 7 的错误报告提交,因为 AFAIK 它是旨在阻止这种情况的发生和(b)持有参考的罪魁祸首。

如果您将原因缩小到捆绑在部署存档中的依赖项,则将其移动到 JBoss AS 7 模块中可能会解决问题。为它创建一个 JBoss 模块,将其部署到modulesAS7 的目录,并通过您的Manifest.MF或通过jboss-deployment-structure.xml. 请参阅有关AS7 类加载器的文档

这就是为什么 Jigsaw 项目被推迟的事实让我感到难过。Java需要一个强大的模块系统来摆脱这种垃圾。

于 2012-07-31T07:51:03.980 回答
8

虚拟机参数为:

-XX:MaxPermSize=256M

只要让它足够大,你就不会达到极限。

从广义上讲,perm gen 内存用于与类和实习字符串相关的对象。除非你使用了很多不同的类,否则你不应该用完。

于 2012-07-25T09:27:21.213 回答
4

我们必须做完全相同的事情,不幸的是,visualvm 并没有为我们做太多事情。

我们最终使用 eclipse mat 分析了崩溃时生成的堆转储,然后查看了leak suspects报告,该报告告诉我们有很多ModuleClassLoader实例被泄露。

单击概述选项卡中的一个实例,然后选择merge shortest paths to GC roots+exclude weak references给了我们我们的罪魁祸首,即不允许这些ModuleClassLoader实例被 GC'ed!

https://smalldata.tech/blog/2015/09/29/detecting-java-permgen-memory-leak

于 2015-09-29T14:02:15.023 回答
2

为什么会发生?

当 Java 虚拟机在永久代中耗尽内存时,会发生“PermGen”错误。回想一下,Java 有一个分代垃圾收集器,有四代:eden、young、old 和 Permanent。在伊甸园时代,对象的生命周期很短,垃圾回收又快又频繁。年轻代由在伊甸代幸存下来的对象组成(或者因为在分配时伊甸代已满而被下推到年轻代),年轻代中的垃圾收集不太频繁,但仍然定期发生(前提是您的应用程序实际上会时不时地做一些事情并分配对象)。老一代,好吧,你想通了。它包含在年轻代中幸存下来的对象,或者被推倒的对象,垃圾收集甚至更少见,但仍然可能发生。最后是永久一代。这是针对虚拟机决定支持永生的对象——这正是问题的核心。永久代中的对象永远不会被垃圾回收;也就是说,在正常情况下,当 jvm 以正常的命令行参数启动时。因此,当您重新部署 Web 应用程序时会发生什么,您的 WAR 文件被解压缩,并且它的类文件被加载到 jvm 中。事情是这样的:几乎总是以永久代结束......(取自:这是针对虚拟机决定支持永生的对象——这正是问题的核心。永久代中的对象永远不会被垃圾回收;也就是说,在正常情况下,当 jvm 以正常的命令行参数启动时。因此,当您重新部署 Web 应用程序时会发生什么,您的 WAR 文件被解压缩,并且它的类文件被加载到 jvm 中。事情是这样的:几乎总是以永久代结束......(取自:这是针对虚拟机决定支持永生的对象——这正是问题的核心。永久代中的对象永远不会被垃圾回收;也就是说,在正常情况下,当 jvm 以正常的命令行参数启动时。因此,当您重新部署 Web 应用程序时会发生什么,您的 WAR 文件被解压缩,并且它的类文件被加载到 jvm 中。事情是这样的:几乎总是以永久代结束......(取自:您的 WAR 文件已解压缩并将其类文件加载到 jvm 中。事情是这样的:几乎总是以永久代结束......(取自:您的 WAR 文件已解压缩并将其类文件加载到 jvm 中。事情是这样的:几乎总是以永久代结束......(取自: http://rlogiacco.blogspot.com/2009/02/jboss-and-permgen-outofmemoryerror.html

这里有一些建议:

将此参数用于您的 JVM。他们告诉垃圾收集器也在 PermGen 上调用它的算法。

set JAVA_OPTS=-Xms512m -Xmx1024m 
-XX:PermSize=512m 
-XX:MaxPermSize=1024m 
-XX:+UseConcMarkSweepGC 
-XX:+CMSPermGenSweepingEnabled 
-XX:+CMSClassUnloadingEnabled 
  • CMSPermGenSweepingEnabled设置包括垃圾回收运行的 PermGen。默认情况下,PermGen 空间永远不会包含在垃圾回收中(因此会无限增长)。
  • CMSClassUnloadingEnabled设置告诉 PermGen 垃圾收集扫描对类对象执行操作默认情况下,类对象获得豁免,即使在垃圾回收期间访问 PermGen 空间也是如此。

重新启动 JBOSS,因为每次部署应用程序时,都会增加 PermGen 中的数据量。

您还可以使用 JRocket JVM 代替 Sun JVM。它的垃圾收集器算法中没有任何 PermGen。

于 2016-10-25T10:26:35.287 回答