我们正在运行一个在 Tomcat 下运行的小型 Web 应用程序,该应用程序编写为 JRuby on Rails。我们正在使用与另一个生产 Web 应用程序共享的 Spring 后端。不幸的是,我们不断遇到 PermGen 问题。
操作系统:Ubuntu Linux 2.6.24-24-server #1 SMP x86_64 GNU/Linux Java:1.6.0_21 Tomcat:6.0.28 JRuby:1.5.0 Rails:2.3.7
我们目前正在被谷歌、雅虎和百度抓取,因此网站使用率上升。我一直在使用 JConsole 监视 Tomcat,我们肯定看到了过多类的问题。当 tomcat 启动时,我们加载了大约 12,000 个类。8 小时后,我们加载了近 75,000 个课程。PermGen 同时从 100MB 增加到 460MB。
类卸载正在工作,但它仅在同一 8 小时内卸载了约 500 个类。PermGen 似乎永远不会被收集。
我们正在使用 Tomcat 的以下 VM 选项运行:
-Xms2048m -Xmx2048m -XX:MaxPermSize=512m -XX:PermSize=128m \
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=4 \
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
显然有某种泄漏。问题是如何在哪里?关于如何追查谁和什么对此负责的任何建议?我希望这是我们的一些非常愚蠢的错误,但我不知道从哪里开始。
任何建议将不胜感激。
编辑
看起来我们正在为每个传入的请求创建一个新类。
编辑 2
它肯定与 JRuby 有关。使用 JConsole,我为类加载器启用了详细模式。这是来自 catalina.out 的示例:
[Loaded anon_class1275113147_895127379 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar]
[Loaded anon_class1354333392_895127376 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar]
[Loaded anon_class1402528430_895127373 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar]
所以问题变成了我如何追踪负责创建这些额外课程的一方?
编辑 3
不确定这是否是问题所在,但不知何故,我们最终得到了大量的类加载器。跑jmap -permstat PID
了,得到:
class_loader classes bytes parent_loader alive? type
total = 1320 135748 947431296 N/A alive=1, dead=1319 N/A
这似乎有点过分了。大多数是以下三种类加载器之一sun.reflect.DelegatingClassLoader
:org.jruby.util.JRubyClassLoader
或org.jruby.util.ClassCache$OneShotClassLoader
。同样,示例输出来自jmap -permstat
:
class_loader classes bytes parent_loader alive? type
0x00007f71f4e93d58 1 3128 0x00007f71f4d54680 dead sun/reflect/DelegatingClassLoader@0x00007f72ef9a6dc0
0x00007f721e51e2a0 57103 316038936 0x00007f720431c958 dead org/jruby/util/JRubyClassLoader@0x00007f72f2fd1158
0x00007f72182f2b10 4 12944 0x00007f721d7f3030 dead org/jruby/util/JRubyClassLoader@0x00007f72f2fd1158
0x00007f721d7d50d8 9 457520 0x00007f720431c958 dead org/jruby/util/ClassCache$OneShotClassLoader@0x00007f72f3ce2368