2010-07-16 24 views
5

Chúng tôi đang chạy một ứng dụng web nhỏ được viết JRuby trên Rails đang chạy dưới Tomcat. Chúng tôi đang sử dụng back-end Spring được chia sẻ với một ứng dụng web sản xuất khác. Thật không may, chúng tôi tiếp tục gặp vấn đề với PermGen.Theo dõi vấn đề PermGen với JRuby trên Rails trong Tomcat

Hệ điều hành: 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

Chúng tôi hiện đang được Google, Yahoo và Baidu thu thập dữ liệu để sử dụng trang web tăng lên. Tôi đã theo dõi Tomcat với JConsole và chúng tôi chắc chắn đang gặp vấn đề với một số lượng quá nhiều lớp học. Khi tomcat khởi chạy, chúng tôi có khoảng 12.000 lớp được tải. Sau 8 giờ, chúng tôi có gần 75.000 lớp được tải. PermGen tăng từ 100MB lên 460MB cùng một lúc.

Việc xếp lớp đang hoạt động nhưng chỉ tải xuống ~ 500 lớp trong cùng khoảng thời gian 8 giờ đó. PermGen dường như không bao giờ được thu thập.

Chúng tôi đang chạy với các tùy chọn VM sau cho Tomcat:

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

Rõ ràng có một số loại rò rỉ. Câu hỏi đặt ra là ở đâu? Bất kỳ lời khuyên nào về cách theo dõi ai và ai chịu trách nhiệm về điều này? Tôi hy vọng đó là một số sai lầm thực sự ngớ ngẩn về phía chúng tôi, nhưng tôi không chắc chắn nên bắt đầu từ đâu.

Mọi lời khuyên sẽ được đánh giá cao.

EDIT

Dường như chúng ta đang thấy một lớp mới được tạo ra cho mọi yêu cầu đến duy nhất.

EDIT 2

Nó chắc chắn liên quan đến JRuby. Sử dụng JConsole, tôi đã kích hoạt chế độ Verbose cho trình nạp lớp. Đây là mẫu từ 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] 

Vì vậy, câu hỏi sẽ trở thành cách tôi theo dõi bên chịu trách nhiệm tạo các lớp bổ sung đó?

EDIT 3

Không chắc nếu điều này vấn đề, nhưng bằng cách nào đó chúng ta kết thúc với một số điên của bộ tải lớp. Ran jmap -permstat PID và đã nhận:

class_loader classes bytes  parent_loader alive?    type 
total = 1320 135748 947431296 N/A    alive=1, dead=1319 N/A 

Điều đó có vẻ hơi quá mức. Phần lớn là một trong ba loại trình nạp lớp: sun.reflect.DelegatingClassLoader, org.jruby.util.JRubyClassLoader hoặc org.jruby.util.ClassCache$OneShotClassLoader. Một lần nữa, đầu ra mẫu từ 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] 

Trả lời

6

PermGen chắc chắn là một vấn đề với các ứng dụng dựa trên JRuby. Tôi không ngạc nhiên khi CMS không thu thập được nhiều. Thông thường không có rò rỉ bộ nhớ thực sự, mà đúng hơn là ứng dụng chỉ nặng và cứng trên permgen và chưa được giảm mức.

tôi có thể đưa ra một vài lựa chọn:

  1. Bump lên PermGen hơn nữa để xem bạn có thể tìm thấy những điểm san lấp mặt bằng-off.
  2. Xem bạn có thể thoát khỏi ứng dụng của mình ở chế độ thuần giải thích (-Djruby.compile.mode = OFF) hay không. Điều đó sẽ loại bỏ một phần lớn các lớp làm đầy permgen của bạn.
  3. Hãy thử chạy với Rails 2.2 và lớn hơn chế độ threadsafe!. Chạy ứng dụng của bạn trong một thời gian chạy là một cách khác để tiết kiệm bộ nhớ lớn, và điều đó cũng áp dụng cho permgen.

EDIT: FYI, câu hỏi này hóa ra là a JRuby bug. Bản phát hành 1.5.2 và 1.6 nên khắc phục sự cố cụ thể này. Ý kiến ​​của tôi ở trên vẫn đứng chung.

+0

75.000 lớp học cho 4 trường hợp vẫn còn có vẻ như rất nhiều với tôi ... :) Nhưng tôi chắc chắn sẽ làm một số thử nghiệm nhiều hơn nữa. Cảm ơn! – organicveggie

+0

Điều liên quan đến tôi là có vẻ như mọi yêu cầu mới tạo ra các lớp mới chưa bao giờ được phát hành. Điều đó không cảm thấy đúng với tôi. Tôi hiểu lý do tại sao JRuby đang tạo ra các lớp học mới, nhưng tôi không thể hiểu tại sao họ không được làm sạch ... – organicveggie

+0

Làm cách nào để biết liệu jruby.compile.mode = OFF có hoạt động chính xác không? Tôi đã thêm -Djruby.compile.mode = OFF vào CATALINA_OPTS trong catalina.sh, bắt đầu tomcat và khởi chạy một webcrawler chống lại trang web. JConsole chỉ ra rằng tổng số lớp và tổng số permgen đang tăng đều đặn. – organicveggie

1

Có các công cụ định hình và những người biết cách sử dụng chúng. Tôi không phải là một trong số họ, tôi sợ.

Lời khuyên về lực lượng vũ phu:

Khởi động lại Tomcat của bạn sau 8 giờ. Tổng thời gian ngừng hoạt động mà người dùng của bạn nhìn thấy sẽ rất chấp nhận được.Vấn đề giải quyết;)


EDIT

Oh, được rồi! The boring solution.

+0

Cảm ơn, nhưng khởi động lại Tomcat cứ sau 8 giờ không phải là giải pháp có thể chấp nhận được. Đặc biệt là vì tôi là người sẽ làm điều đó. :) Điểm ở đây là giải quyết vấn đề cơ bản. – organicveggie

+0

'cron' và bạn bè được phát minh để bạn không cần phải khởi động lại theo cách thủ công. Bạn có một vấn đề hoặc là trong mã Ruby của bạn hoặc việc thực hiện JRuby hoặc một sự kết hợp tối nghĩa của điều đó và Tomcat, vì vậy nó thậm chí không chỉ là một vấn đề bộ nhớ Java "chuẩn". Tôi đoán rằng bạn sẽ khởi động lại máy chủ của bạn nhiều lần trước khi bạn nhận được nó cố định, vì vậy nó sẽ là khôn ngoan để nhìn vào kịch bản khởi động lại thường xuyên. –

+0

Tuyệt đối. Nhưng tôi khá thoải mái với quá trình dừng & bắt đầu tomcat và tự động hóa điều đó. Chúng ta có quyền kiểm soát. Nhưng thời gian chết mỗi 8 giờ, ngay cả khi nó chỉ 30-60 giây, không được coi là chấp nhận được để quản lý. Bạn biết làm thế nào nó đi. :) Vì vậy, câu hỏi của tôi ở đây là thực sự tìm kiếm lời khuyên về cách theo dõi nguồn gốc của vấn đề. – organicveggie

2

Chúng tôi đã gặp sự cố tương tự với ứng dụng web Sinatra bằng cách sử dụng tùy chọn JRuby 1.5.1: JVM TraceClassLoading in ra anon_class * được tải cùng với mọi yêu cầu. Sau khi chi tiêu đôi khi để thu hẹp nơi mà lớp vô danh đã được nạp, được thực hiện bằng cách in ra các câu lệnh theo dõi cho bàn điều khiển, cuối cùng chúng tôi đã phát hiện ra rằng nó đã gây ra bằng cách gọi một phương thức còn thiếu trên một đối tượng Java.

Cuộc gọi đó đã kích hoạt JRuby thêm phương thức bị thiếu đó vào đối tượng Java. Quá trình đó tạo ra một lớp JRuby singleton mới, được đặt tên là "anon_class" theo sau bởi một số giá trị băm. Vì nó là một loại lớp, nó nằm trong PermGen và không bao giờ được thu thập bởi GC.

Giải pháp thay thế là tránh gọi phương thức thiếu hoặc cung cấp triển khai. Trong trường hợp của chúng tôi, chúng tôi đã cố gọi phương thức sắp xếp bằng một khối trên đối tượng Java ArrayList. Nếu chúng ta gọi phương thức "to_a" đầu tiên để chuyển đổi Java ArrayList thành mảng JRuby, thì sắp xếp với một khối sẽ không tạo ra một lớp anon_class.

Vì vậy, tôi khuyên bạn nên xem lại mã cho các địa điểm truy cập đối tượng Java từ JRudy.

2

Chỉ cần để cung cấp một ví dụ đơn giản để hiển thị vấn đề này và cách giải quyết:

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 

Giả sử tên tập tin là test_classload.rb, các kết quả sau được: $ JRuby -J-XX: + TraceClassLoading test_classload.rb | grep anon_class
[Loaded anon_class819349464_307995535 từ JVM_DefineClass]
[anon_class729155693_307995574 Loaded từ JVM_DefineClass]
[anon_class1690464956_307995577 Loaded từ JVM_DefineClass]

Nếu chuyển sang dòng nhận xét, kết quả là rỗng: không anon_class nạp vào.

Các vấn đề liên quan