在Tomcat中使用JRuby on Rails追踪PermGen问题

在Tomcat中使用JRuby on Rails追踪PermGen问题,tomcat,memory-leaks,jruby,jrubyonrails,permgen,Tomcat,Memory Leaks,Jruby,Jrubyonrails,Permgen,我们正在运行一个在Tomcat下运行的JRuby on Rails编写的小型web应用程序。我们正在使用一个与另一个生产web应用程序共享的弹性后端。不幸的是,我们一直遇到永久性的问题 操作系统: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 轨道:2.3.7 我们目前正在被谷歌、雅虎和百度抓取,所以网站使用率正在上升。我一直在用JConsole监视Tomca

我们正在运行一个在Tomcat下运行的JRuby on Rails编写的小型web应用程序。我们正在使用一个与另一个生产web应用程序共享的弹性后端。不幸的是,我们一直遇到永久性的问题

操作系统: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 轨道:2.3.7

我们目前正在被谷歌、雅虎和百度抓取,所以网站使用率正在上升。我一直在用JConsole监视Tomcat,我们肯定看到了类数量过多的问题。当tomcat发布时,我们已经加载了大约12000个类。8小时后,我们加载了将近75000个类。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.jrubycassloader
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

有分析工具,也有知道如何使用它们的人。恐怕我不是他们中的一员

暴力建议:

每8小时重新启动一次Tomcat。用户看到的总停机时间是可以接受的。问题已解决;)


编辑


哦,好吧

对于基于JRuby的应用程序,PermGen肯定是个问题。我并不惊讶CMS收集的信息不多。通常情况下,不会出现真正的内存泄漏,但应用程序在permgen上的性能很差,还没有稳定下来

我可以提供以下几种选择:

  • 把permgen再往上推一点,看看你是否能找到平衡点
  • 看看是否可以在纯解释模式下运行应用程序(-Djruby.compile.mode=OFF)。这应该可以消除填充permgen的大量类
  • 尝试使用Rails 2.2及更高版本运行
    threadsafe模式。在单个运行时运行应用程序是另一种节省大量内存的方法,这也适用于permgen

  • 编辑:仅供参考,这个问题原来是。1.5.2和1.6版本应修复此特定问题。我上面的评论仍然是一般性的。

    使用JRuby 1.5.1的Sinatra web应用程序也有类似的问题: JVM TraceClassLoading选项打印出随每个请求一起加载的一个non_类*

    我们花了一些时间来缩小匿名类的加载范围,这是通过将跟踪语句打印到控制台来完成的,最后我们发现这是由于调用Java对象上缺少的方法造成的

    该调用触发JRuby将缺少的方法添加到Java对象中。这个过程创建了一个新的单例JRuby类,名为“anon_类”,后面跟着一些散列值。因为它是一个类类型,所以它停留在PermGen中,永远不会被GC收集

    解决方法是避免调用缺少的方法或提供实现。在我们的例子中,我们试图用Java ArrayList对象上的一个块调用sort方法。如果我们首先调用“to_a”方法将Java ArrayList转换为JRuby array,那么使用块排序将不会创建anon_类


    因此,我建议查看从JRuby访问Java对象的地方的代码。

    只是提供一个简单的示例来说明这个问题和解决方法:

    require 'java'
    include_class java.util.ArrayList
    
    list = ArrayList.new
    list << 3
    list << 2
    list << 1
    
    3.times do
      new_list = list.sort { |a, b| a <=> b}
      #new_list = list.to_a.sort { |a, b| a <=> b}
      puts new_list
    end
    
    require'java'
    包含类java.util.ArrayList
    list=ArrayList.new
    
    谢谢,但是每8小时重新启动Tomcat不是一个可接受的解决方案。特别是因为我是将要这样做的人。:)这里的重点是解决潜在的问题。
    cron
    和friends是发明出来的,因此您不需要手动重新启动。您的Ruby代码或JRuby的实现中存在问题,或者JRuby与Tomcat的模糊组合中存在问题,因此它甚至不仅仅是一个“标准”Java内存问题。我猜,在修复服务器之前,您将多次重新启动服务器,因此,明智的做法是查看定期重新启动脚本。但是我对停止和启动tomcat并使其自动化的过程非常满意。我们已经控制住了。但每8小时停机一次,即使只有30-60秒,管理层也不会接受。你知道怎么回事。:)所以我这里的问题其实是在寻找如何追踪问题根源的建议。一旦他们听到我的咨询费率,他们会很高兴每天有3分钟的停机时间!;)但说真的,你的处境很困难。运行“普通”Ruby服务器可能不是一个坏的选择。我能提供的另一个绝望的想法是,让自己在父亲的脚下哭泣。没有人比他更了解JRuby。但是,嘿,你可能首先想看看我链接到的页面:)别开玩笑了,我认为它直接解决了你的问题,包括