【问题标题】:How to debug permgen memory errors for a project that uses sbt and play 2 framework in Scala如何为在 Scala 中使用 sbt 和 play 2 框架的项目调试 permgen 内存错误
【发布时间】:2014-06-04 21:20:15
【问题描述】:

我正在使用具有足够内存和 PermSize 的 sbt(请参阅下面的值),但我仍然不断收到内存错误。

[error] java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: PermGen space

我的 sbt 脚本如下所示。

SBT_OPTS="-Xms1024M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=4028M" java $SBT_OPTS -jar `dirname $0`/sbt-launch.jar "$@"

我正在 IntelliJ 13 IDE 中进行更改,然后使用以下命令运行我的 Play 应用程序

$ ./sbt
[name-of-my-play-project] $ compile
[name-of-my-play-project] $ ~run

我也试过run 而不是~run。如果我使用 $./sbt 后跟 ~run 进行冷启动,我可以运行项目并访问页面。但是,经过一些代码更改后,Play 不再响应。

我尝试在另一台机器(也是 OS-X)上运行相同的项目,但我得到了类似的行为。我担心它不会一直发生,但经常发生以至于它让我没有效率,因为我必须重新启动 sbt 并从头开始玩,这完全违背了 Play2 框架的主要好处之一。

如果有人能帮助我从哪里开始寻找我的设置有什么问题,我将不胜感激。

创建项目使用激活器,然后开始使用 sbt 为它。

更新 - YourKit 的屏幕截图(基于 @James 的建议)显示内存泄漏。

更新 2 YourKit 显示死锁(请参阅随附的屏幕截图)。我不确定这是否是问题的根本原因。我把它放在这里,以防有人可以提供更多关于正在发生的事情的见解。

更新 3 - 解决方案 经过超过 12 小时的不间断调试,我终于能够弄清楚了。这是一件非常简单的事情,我将在最后解释。由于我的代码中有很多 3rd 方库/插件(ReactiveMongo、Redis、...),我不确定其中是否有任何导致 OutOfMemory 错误。所以我只是删除了所有不需要的代码,直到我得到一个简单的控制器,其中一个方法只返回一个简单的“Hello world”。这个简单的程序我仍然遇到同样的错误。这意味着我的配置有问题。我尝试在我的project/build.properties 中将 sbt 版本更改为 0.13.5 (sbt.version=0.13.5),但仍然没有解决。我下载了新的 sbt-launch.jar (0.13.5),问题依然存在。同样,始终不变的一件事是错误从未改变。因此,由于某种原因,未设置 sbt 的标志。

我的 sbt bash 脚本看起来像(一行中的所有内容) -

SBT_OPTS="-Xms1024M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=4028M" java $SBT_OPTS -jar `dirname $0`/sbt-launch.jar "$@"

我改成

SBT_OPTS="-Xms1024M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=4028M" 
java $SBT_OPTS -jar `dirname $0`/sbt-launch.jar "$@"

这解决了它。同样是类加载问题,但不是在我的代码中,而是在 sbt 的配置中。

【问题讨论】:

  • 使用带有 G1 且不带 PermSize 的 java 8,或带有 G1 的 java 7。见Class Unloading in Java's G1 Garbage Collector (G1GC)
  • 存在内存泄漏,使用不同的 JVM 或垃圾收集器不会修复内存泄漏,在 Java 8 中,这只是意味着它会占用系统内存,直到你用完所有内存系统崩溃。

标签: scala playframework-2.0 out-of-memory sbt permgen


【解决方案1】:

您看到的是类加载器泄漏。类加载器泄漏的可能原因有很多。最常见的是启动线程而不是关闭它们。此外,线程本地、类加载器之外的缓存和关闭挂钩也是常见的罪魁祸首。

附加一个分析器(我推荐YourKit)并观察每次重新加载后线程会发生什么。如果创建了新线程但未关闭,则找出它们是哪些线程,启动它们的原因,并确保由 Global.onStop 清理。

如果这没有帮助,请进行堆转储,然后将其加载到 YourKit 中。现在查找您希望仅是其中之一或数量过多的类的多个实例。找到该类的合并 GC 根,沿着该链的某处应该是内存泄漏的来源。

【讨论】:

  • 感谢您的建议。我使用了 Yourkit,它在加载类时确实显示了内存泄漏(请参阅上面的屏幕截图)。我正在控制器中创建自己的演员系统。我用import play.libs.Akka 替换了它,但它仍然没有帮助。我正在使用其他几个可能正在创建线程的库(reactivemongo、twitter4j)。我会弄清楚并尝试在 Global.onStop 上停止/清理它们
  • 您在此处显示的屏幕截图并未显示内存泄漏,它仅显示了内存不足时尝试分配内存(Scala 编译器)时发生的情况。无论如何,看起来你解决了它 - 没有内存泄漏,只是由于配置错误,你没有为 JVM 分配足够的内存来加载类。
  • 我给了足够的内存。问题是这些类没有被卸载,因为这个标志 (-XX:+CMSClassUnloadingEnabled) 没有设置。在此更改之后,我已经完美运行,并且很高兴不必担心在每次小的更改后重新启动服务器 :-)!
猜你喜欢
  • 2015-01-19
  • 2022-01-23
  • 2014-12-04
  • 2014-04-07
  • 2014-06-14
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 2013-04-03
相关资源
最近更新 更多