2010-07-16 22 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加載。