Lift和Jetty中的PermGen问题

41

我正在使用标准的Lift平台(maven和jetty)进行开发。我每隔几天就会反复遇到这个问题:

Exception in thread "7048009@qtp-3179125-12" java.lang.OutOfMemoryError: PermGen space
2009-09-15 19:41:38.629::WARN:  handle failed
java.lang.OutOfMemoryError: PermGen space

这是在我的开发环境中。这不是个问题,因为我可以不停地重启服务器。在部署时我没有遇到这些问题,所以这不是一个真正的问题。我只是好奇。

我对JVM不太了解。我认为我正确地认为永久代内存用于类和interned字符串等东西?我记得的有点混淆了与.NET内存模型...

这是为什么呢?默认值是否设置得过低?是否与Scala必须为Function对象和类似FP的事物创建的所有辅助对象有关?每次我重新启动Jetty并使用新编写的代码(每几分钟一次),我想它会重新加载类等。但即使如此,也不可能那么多吧?而且JVM难道不能处理大量的类吗?

谢谢

Joe


1
正如 https://web.archive.org/web/20150105090518/http://www.scala-blogs.org/2007/12/scala-statically-typed-dynamic-language.html 文章中所提到的:“JVM 的分代垃圾回收器是针对这种情况进行优化的,因为隐式创建的对象寿命短暂,它们会被很好地回收。” 但如果这些对象的寿命不短暂,那就另当别论了。 - VonC
5个回答

44

来自这篇文章

这个异常出现有一个简单的原因:
permgenspace是Java虚拟机中存储类属性(如方法、字段、注释以及静态变量等)的空间,但这个空间有一点特殊,就是垃圾收集器不会清理它。 因此,如果您的Web应用程序使用或创建了许多类(我想到的是动态生成类),那么您很可能遇到此问题。 这里有一些解决方案可以帮助我摆脱这个异常:

  • -XX:+CMSClassUnloadingEnabled:启用permgenspace中的垃圾收集
  • -XX:+CMSPermGenSweepingEnabled:允许垃圾收集器从内存中删除类
  • -XX:PermSize=64M -XX:MaxPermSize=128M:提高分配给permgenspace的内存量

也许这能有所帮助。

编辑于2012年7月(将近3年后):

Ondra Žižka发表评论(我已经更新了上面的答案):

JVM 1.6.0_27说:请使用:

  • CMSClassUnloadingEnabled(在使用CMS GC时是否启用类卸载)
  • 代替CMSPermGenSweepingEnabled

有关更多信息,请参见完整的Hotspot JVM选项 - 完整参考


1
谢谢。我想要调整GC的行为或PermGen的大小来解决问题。我真的很想知道Lift为什么会使PermGen对象膨胀,或者是否有具体的原因。 - Joe
1
可能是由于Lift执行了一些动态类生成的操作。 - VonC
3
JVM 1.6.0_27 提示:未来请使用 CMSClassUnloadingEnabled 替代 CMSPermGenSweepingEnabled。请注意保持原意并使文本更易懂,不提供任何解释或其他信息。 - Ondra Žižka
@gyozokudor,你的意思是你无法还原到此答案的上一个版本来取消你的编辑吗? - VonC
我撤销了我的编辑。我编辑后无法回滚,因为它需要被审核。 - gyozo kudor
显示剩余2条评论

12
如果在运行 mvn jetty:run 命令时出现此问题,请设置 MAVEN_OPTS
对于 Linux:
export MAVEN_OPTS="-XX:+CMSClassUnloadingEnabled -XX:PermSize=256M -XX:MaxPermSize=512M"
mvn jetty:run

对于Windows:

set "MAVEN_OPTS=-XX:+CMSClassUnloadingEnabled -XX:PermSize=256M -XX:MaxPermSize=512M"
mvn jetty:run

现在应该没问题了。如果还有问题,可以增加-XX:MaxPermSize

您也可以将这些设置永久保存到您的环境变量中。

  • 对于Linux,请将export行追加到~/.bashrc

  • 对于Windows,按下Win键+PrintScreen,然后进入高级系统设置>环境变量。 也可参见http://support.microsoft.com/kb/310519


3

这是由于您所建议的类重载而引起的。如果您使用了大量库等,每次重新启动都会快速增加类的数量。尝试使用VisualVM监视Jetty实例,以获得在重载时内存消耗的概述。


2
邮件列表(http://groups.google.com/group/liftweb/)是Lift的官方支持论坛,您可以在那里获得更好的答案。我不知道您的开发设置的细节(您没有提供太多信息),但我假设您正在Jetty中重新加载war文件而没有真正重新启动它。Lift不执行动态类生成(如上面VonC所建议的),但Scala将每个闭包编译为单独的类。如果您在几天内添加和删除闭包到您的代码中,可能会加载太多的类并且永远不会卸载,从而占用perm空间。我建议您启用VonC上面提到的JVM选项,看看它们是否有帮助。

1
谢谢。我通常会在Google群组上发布,但我想试试StackOverflow。我会尝试增加数字。 - Joe

1

永久代是JVM放置那些可能不会被(垃圾)回收的东西,例如自定义类加载器。

根据您部署的内容,永久代设置可能较低。某些应用程序和/或容器组合确实包含一些内存泄漏,因此当应用程序未部署时,有时不会回收一些东西,例如类加载器,导致填充Perm空间,从而生成您遇到的错误。

不幸的是,目前在这种情况下最好的选择是使用以下jvm标志将perm空间最大化(例如192m perm大小):

-XX:MaxPermSize=192M (or 256M)

另一个选择是确保容器或框架不泄漏内存。

MaxPermSize 和 192m 之间需要有一个 '='。 - Scott Morrison

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接