【问题标题】:JBoss 7, java.lang.OutOfMemoryError: PermGen spaceJBoss 7,java.lang.OutOfMemoryError:PermGen 空间
【发布时间】:2012-07-25 09:17:37
【问题描述】:

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

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

为了不再遇到任何问题,哪个值足够好?有什么办法可以永久摆脱这个问题(比如使用不同的虚拟机,比如 JRockit)?

【问题讨论】:

  • 这真的不应该在 AS 7 上发生,除非您要部署很多大类。由于其模块化的类加载器,JBoss AS 7 不应该受到classloader leaks 的影响——结合测试期间的多次重新部署——是其他服务器上此问题的最常见原因。您是否仅在大量重新部署后才收到这些错误?什么情况下?
  • 我们一直在测试我们的应用程序,在这个阶段我们进行了广泛的重新部署。这个问题不知从何而来
  • 有趣的是,我在我的 Windows 服务器上运行 JBoss 7.1.1 作为服务。在这种情况下,部署后它就死了。从standalone.bat 运行服务器,在我的开发机器上运行稳定。

标签: jakarta-ee jboss webserver jboss7.x


【解决方案1】:

由于这种情况是在多次重新部署后发生的,听起来您遇到了classloader leak,一种常见的permgen leak

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

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

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

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

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

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

如果您将原因缩小到捆绑在部署存档中的依赖项,则将其移动到 JBoss AS 7 模块中可能会解决问题。为它创建一个 JBoss 模块,将其部署到 AS7 的 modules 目录,并通过 Manifest.MFjboss-deployment-structure.xml 将对它的依赖添加到您的部署中。见documentation on the AS7 class loader

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

【讨论】:

  • jhat 指的是the approach proposed by Frank Kieviet。但是这种方法不是很方便。更好的方法是 1) 进行堆转储; 2)在Eclipse MAT中打开堆转储; 3) 寻找重复的类; 4)右键单击最重复的类,然后选择“合并最短路径到 GC 根”选项“排除弱引用”。这给出了防止通过类卸载减少永久空间的对象列表。
【解决方案2】:

VM参数为:

-XX:MaxPermSize=256M

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

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

【讨论】:

  • 你知道 JBoss 7 的 MaxPermSize 的默认值吗?
  • 虽然有用,但这对类加载器泄漏没有帮助,只会推迟问题。最终你会用完 Permen 空间。
  • 出于好奇,一个 Web 应用程序有多少个不同的类,100、1000、10000?
  • 顺便说一下我在standalone.conf中这样做
【解决方案3】:

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

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

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

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

【讨论】:

    【解决方案4】:

    为什么会这样?

    当 Java 虚拟机用完时,会发生“PermGen”错误 永久代中的记忆。回想一下,Java 有一个 分代垃圾收集器,有四代:伊甸园,年轻, 旧的和永久的。在伊甸园世代中,对象很短 生活和垃圾收集是迅速和经常的。年轻一代 由在伊甸园一代中幸存下来的对象组成(或被推送 直到年轻,因为伊甸园那一代人在 分配),年轻代的垃圾收集较少 频繁但仍然以相当规律的时间间隔发生(前提是 您的应用程序实际上做了一些事情并分配对象 时不时)。老一代,好吧,你想通了。它包含 在年轻一代中幸存下来的对象,或者被推倒的对象, 垃圾收集甚至更少见,但仍然可能发生。 最后是永久一代。这是针对对象 虚拟机已经决定用永生来背书——也就是 恰恰是问题的核心。永久代中的对象 永远不会被垃圾收集;也就是说,在正常情况下,当 jvm 使用正常的命令行参数启动。所以呢 当您重新部署您的 Web 应用程序时发生的情况是,您的 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。

    【讨论】:

      猜你喜欢
      • 2011-07-28
      • 2015-05-11
      • 2013-09-01
      • 2011-04-14
      • 2012-10-21
      • 2012-08-05
      • 1970-01-01
      • 2014-04-17
      • 1970-01-01
      相关资源
      最近更新 更多