2014-06-11 11 views
9

Hôm qua tôi đã triển khai ứng dụng Grails (2.3.6) đầu tiên của mình cho một máy chủ dev và bắt đầu theo dõi nó. Tôi chỉ có một màn hình tự động nói rằng CPU đã được gắn trên máy tính này, và vì vậy tôi đã SSH vào nó. Tôi chạy top và phát hiện ra rằng đó là PID của ứng dụng Java của tôi đang ghim máy chủ. Tôi cũng nhận thấy trí nhớ là 40%. Sau một vài giây, CPU đã ngừng ghim, giảm xuống mức bình thường và bộ nhớ quay trở lại khoảng ~ 20%. GC chính cổ điển.Hiểu rò rỉ bộ nạp lớp Groovy/Grails

Trong khi thu thập, tôi đã tạo một vùng lưu trữ. Sau GC, sau đó tôi mở bãi chứa trong JVisualVM và thấy rằng hầu hết bộ nhớ đã được cấp phát cho một lớp org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry. Có gần 250.000 trường hợp trong số này, ăn khoảng 25 MB bộ nhớ.

Tôi googled lớp này và đã xem nó là ultra helpful Javadocs. Vì vậy, tôi vẫn không biết lớp này làm gì.

Nhưng googling nó cũng đưa ra khoảng một tá bài báo liên quan (một số câu hỏi SO) liên quan đến lớp này và rò rỉ lớp PermGen/classloader với các ứng dụng Grails/Groovy. Và mặc dù có vẻ như ứng dụng của tôi đã thực sự làm sạch các phiên bản 250K này với GC, nó vẫn gây rắc rối khi có quá nhiều phiên bản của nó và GC đã ghim CPU trong hơn 5 phút.

Câu hỏi của tôi:

  • lớp này là gì và những gì là Groovy làm với nó?
  • Ai đó có thể giải thích this answer cho tôi? Tại sao -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled giúp vấn đề cụ thể này?
  • Tại sao lớp học này đặc biệt phiền hà cho PermGen?

Trả lời

12

Groovy là ngôn ngữ động, mọi cuộc gọi phương thức đều được gửi động. Để tối ưu hóa Groovy đó, hãy tạo MetaClass cho mỗi java.lang.Class trong MetaClassRegistry. Các trường hợp MetaClass này được tạo theo yêu cầu và được lưu trữ bằng cách sử dụng các tham chiếu yếu.

Lý do bạn nhìn thấy rất nhiều org.codehaus.groovy.runtime.metaclass.MetaMethodIndex.Entry là vì Groovy lưu trữ một bản đồ các lớp và phương thức trong bộ nhớ để chúng có thể được gửi nhanh chóng theo thời gian chạy. Tùy thuộc vào kích thước của ứng dụng, điều này có thể giống như bạn đã phát hiện ra hàng ngàn lớp vì mỗi lớp có thể có hàng chục đôi khi hàng trăm phương thức.

Tuy nhiên, không có "rò rỉ bộ nhớ" trong Groovy và Grails, những gì bạn thấy là hành vi bình thường. Ứng dụng của bạn sắp hết bộ nhớ, có thể do bộ nhớ chưa được cấp đủ bộ nhớ, điều này sẽ khiến các trường hợp MetaClass bị thu gom rác. Bây giờ, ví dụ: bạn có vòng lặp:

for(str in strings) { 
    println str.toUpperCase() 
} 

Trong trường hợp này, chúng tôi đang gọi phương thức trên lớp String. Nếu bạn đang chạy thấp trên bộ nhớ những gì sẽ xảy ra là cho mỗi lần lặp của vòng lặp MetaClass sẽ được thu thập rác và sau đó tái tạo lại cho lần lặp tiếp theo. Điều này có thể làm chậm đáng kể một ứng dụng và dẫn đến CPU bị ghim như bạn đã thấy. Trạng thái này thường được gọi là "di chuyển metaclass" và là dấu hiệu cho thấy ứng dụng của bạn sắp hết bộ nhớ heap.

Nếu Groovy không rác thu thập những metaclass trường thì có mà có nghĩa là có một rò rỉ bộ nhớ trong Groovy, nhưng thực tế là nó được rác thu thập các lớp này là một dấu hiệu cho thấy tất cả là tốt, trừ thực tế là bạn chưa phân bổ đủ bộ nhớ heap ngay từ đầu. Đó không phải là để nói rằng có thể có một rò rỉ bộ nhớ trong một phần khác của ứng dụng đang ăn hết tất cả bộ nhớ có sẵn và không đủ để Groovy hoạt động chính xác.

Đối với câu trả lời khác mà bạn tham khảo, việc thêm loại bỏ lớp và các chỉnh sửa PermGen sẽ không thực sự làm bất cứ điều gì để giải quyết vấn đề bộ nhớ của bạn trừ khi bạn phân tích động các lớp khi chạy. Không gian PermGen được JVM sử dụng để lưu trữ các lớp được tạo động. Groovy cho phép bạn biên dịch các lớp trong thời gian chạy sử dụng GroovyClassLoader.parseClass hoặc GroovyShell.evaluate. Nếu bạn liên tục phân tích các lớp thì có thêm các cờ dỡ lớp có thể giúp bạn. Xem thêm bài viết này:

Locating code that is filling PermGen with dead Groovy code

Tuy nhiên, một Grails ứng dụng điển hình không động biên dịch các lớp học trong thời gian chạy và do đó tinh chỉnh PermGen và các thiết lập lớp dỡ sẽ không thực sự đạt được bất cứ điều gì.

Bạn nên xác minh xem bạn đã cấp đủ bộ nhớ heap bằng cờ -Xmx chưa và phân bổ nhiều hơn.