2010-07-16 23 views
5

我们正在运行一个小型的Web应用程序,它编写了在Tomcat下运行的JRuby on Rails。我们正在使用与其他生产Web应用程序共享的Spring后端。不幸的是,我们不断遇到PermGen的问题。在Tomcat中跟踪JRuby on Rails上的PermGen问题

操作系统:Ubuntu Linux操作系统2.6.24-24服务器#1 SMP x86_64的GNU/Linux的 的Java:1.6.0_21 的Tomcat:6.0.28 的JRuby:1.5.0 的Rails:2.3.7

目前,我们正在通过Google,Yahoo和百度进行抓取,因此网站的使用情况已经提升。我一直在用JConsole监视Tomcat,并且我们肯定会看到类的数量过多的问题。当tomcat启动时,我们有大约12,000个类加载。 8小时后,我们有近75,000个课程加载。 PermGen在同一时间从100MB增加到460MB。

班级卸班正在工作,但它只在相同的8小时内卸载了500班。 PermGen似乎从未收集过。

我们用下面的VM选项为Tomcat运行:

-Xms2048m -Xmx2048m -XX:MaxPermSize=512m -XX:PermSize=128m \ 
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=4 \ 
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled 

显然有某种泄漏。问题在哪里?有关如何追查谁和对此负责的任何建议?我希望这是我们的一个非常愚蠢的错误,但我不确定从哪里开始。

任何意见将不胜感激。

编辑

它看起来像我们看到的每一个传入请求创建一个新的类。

EDIT 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.JRubyClassLoaderorg.jruby.util.ClassCache$OneShotClassLoader。再次,从jmap -permstat样本输出:

class_loader   classes bytes  parent_loader   alive? type 
0x00007f71f4e93d58  1  3128  0x00007f71f4d54680  dead sun/reflect/[email protected] 
0x00007f721e51e2a0  57103 316038936 0x00007f720431c958  dead org/jruby/util/[email protected] 
0x00007f72182f2b10  4  12944  0x00007f721d7f3030  dead org/jruby/util/[email protected] 
0x00007f721d7d50d8  9  457520  0x00007f720431c958  dead org/jruby/util/[email protected] 

回答

6

PermGen的肯定是与基于JRuby的应用问题。我不惊讶CMS不收集太多。通常情况下,没有真正的内存泄漏,但是应用程序对于permgen来说只是沉重而难以处理的,而且还没有稳定下来。

我可以提供几个选择:

  1. 颠簸起来的PermGen更进一步,看看是否能找到练级客点。
  2. 看看你能不能用纯解释模式下运行的应用程序脱身(-Djruby.compile.mode = OFF)。这应该摆脱填充你的permgen类大块。
  3. 尝试使用Rails 2.2及更高版本threadsafe!模式运行。在单个运行时中运行应用程序是另一种节省大量内存的方式,也适用于permgen。

编辑:仅供参考,这个问题竟然是a JRuby bug。 1.5.2和1.6版本应该解决这个特殊问题。我上面的评论仍然一般。

+0

对于4个实例,75,000个类仍然看起来很像我...... :)但是我肯定会做更多的测试。谢谢! – organicveggie 2010-07-16 18:59:46

+0

关注我的事情是,它看起来每个新的请求都会创建永远不会发布的新类。这对我来说并不合适。我明白为什么JRuby会创建新类,但我不明白为什么他们不会清理干净...... – organicveggie 2010-07-16 19:05:53

+0

如何判断jruby.compile.mode = OFF是否正常工作?我在catalina.sh中向CATALINA_OPTS添加了-Djruby.compile.mode = OFF,启动了tomcat并针对该网站启动了一个web爬网程序。 JConsole表明,班级总人数和总人数正在稳步上升。 – organicveggie 2010-07-16 20:43:34

1

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

蛮力忠告:

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


编辑

哦,好吧! The boring solution

+0

谢谢,但每8小时重新启动Tomcat并不是一个可接受的解决方案。特别是因为我是那种会这样做的人。 :)这里的要点是解决潜在的问题。 – organicveggie 2010-07-16 16:37:48

+0

'cron'和朋友们被发明出来,所以你不需要手动重新开始。您有一个问题,无论是在您的Ruby代码中,还是在JRuby的实现中,或者是与Tomcat的模糊组合,所以它甚至不仅仅是“标准”Java内存问题。我猜你会在修复服务器之前重启服务器很多次,所以看看脚本定期重启是明智之举。 – 2010-07-16 16:52:07

+0

当然。但是我对停止和启动tomcat并自动执行这个过程感到非常舒服。我们已经控制住了。但是,即使只有30-60秒,每8小时的停机时间也不被管理人员接受。你知道它是怎么回事。 :)所以我的问题真的在寻找如何追踪问题根源的建议。 – organicveggie 2010-07-16 16:59:47

2

我们也有类似的问题,使用JRuby 1.5.1一个Sinatra的web应用程序:每个请求一起加载 JVM TraceClassLoading选项可输出anon_class *。

花费一段如该匿名类加载得到,通过打印出跟踪语句到控制台,它是为了缩小以后,我们终于想通了,它是通过调用Java对象上缺少方法造成的。

调用触发JRuby中失踪方法添加到Java对象。该进程创建了一个新的单例JRuby类,它被命名为“anon_class”,后面跟着一些哈希值。由于它是一个类类型,它保留在PermGen中,并且永远不会被GC收集。

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

所以,我建议审查代码从JRudy访问Java对象的地方。

2

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

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 

假设文件名是test_classload.rb,以下是输出: $ JRuby的-J-XX:+ TraceClassLoading test_classload。rb | grep的anon_class

[加载从JVM_DefineClass anon_class819349464_307995535] [加载anon_class729155693_307995574从JVM_DefineClass]
[加载anon_class1690464956_307995577从JVM_DefineClass]

如果切换到注释行中,输出是空的:无anon_class加载。