2009-03-18 18 views
43

Tại nơi làm việc, chúng tôi đã gặp sự cố với "PermGen out of memory" ngoại lệ, với trưởng nhóm quyết định đó là lỗi trong JVM - một điều gì đó có liên quan đến việc triển khai mã. Nếu không giải thích nhiều chi tiết, ông chỉ ra rằng triển khai nóng là một "vấn đề khó khăn", quá khó khăn mà ngay cả .NET vẫn chưa làm được.Điều gì khiến triển khai nóng là "vấn đề khó khăn"?

Tôi tìm thấy rất nhiều bài viết giải thích việc triển khai nóng từ tầm mắt của chim, nhưng luôn thiếu các chi tiết kỹ thuật. Bất cứ ai có thể chỉ cho tôi một lời giải thích kỹ thuật, và giải thích tại sao triển khai nóng là "một vấn đề khó khăn"?

+0

Tại sao lại là Wiki Cộng đồng này? – Eddie

+2

Nghĩ rằng nó có thể giúp đỡ trong trường hợp tôi để lại một cái gì đó không rõ ràng ... làm điều đó theo mặc định, là nghi thức xấu? –

+0

Hài hước: tìm thấy suy nghĩ-o khi đọc lại ngay bây giờ, cảm ơn! –

Trả lời

58

Khi một lớp được tải, nhiều dữ liệu tĩnh khác nhau về lớp được lưu trữ trong PermGen. Miễn là một tham chiếu trực tiếp đến cá thể Class này tồn tại, cá thể lớp không thể được thu thập rác.

Tôi tin rằng một phần của vấn đề liên quan đến việc liệu GC có nên loại bỏ các phiên bản Class cũ từ perm gen hay không. Thông thường, mỗi lần bạn triển khai nóng, các cá thể lớp mới sẽ được thêm vào nhóm bộ nhớ PermGen và các phiên bản cũ, hiện không sử dụng, thường không bị xóa. Theo mặc định, Sun JVM sẽ không chạy bộ sưu tập rác trong PermGen, nhưng điều này có thể được kích hoạt với các đối số lệnh "java" tùy chọn.

Do đó, nếu bạn triển khai đủ thời gian, cuối cùng bạn sẽ xả không gian PermGen của mình.

Nếu ứng dụng web của bạn không tắt hoàn toàn khi không được triển khai - nếu nó để một Chủ đề đang chạy, ví dụ - thì tất cả các phiên bản Lớp được sử dụng bởi ứng dụng web đó sẽ được ghim vào khoảng trống PermGen. Bạn redeploy và bây giờ có một bản sao toàn bộ của tất cả các trường hợp Class được nạp vào PermGen. Bạn undeploy và Thread tiếp tục, ghim ANOTHER thiết lập các cá thể lớp trong PermGen. Bạn redeploy và tải một tập hợp toàn bộ các bản sao ... và cuối cùng PermGen của bạn đầy lên.

Đôi khi bạn có thể khắc phục điều này bằng cách:

  • Cung cấp luận cứ lệnh để một Sun JVM gần đây để cho phép GC trong PermGen và các lớp học. Đó là: -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
  • Sử dụng một JVM khác nhau mà không sử dụng một kích thước cố định hoặc PermGen mà không GC trên lớp được nạp

Nhưng điều này sẽ giúp chỉ nếu ứng dụng web của bạn tắt hoàn toàn và sạch sẽ, không để lại tham chiếu trực tiếp đến bất kỳ phiên bản Lớp nào của bất kỳ Lớp nào được tải bởi Trình tải lớp cho Ứng dụng web đó.

Thậm chí điều này sẽ không nhất thiết khắc phục được sự cố, do rò rỉ bộ nạp lớp học. (Cũng như quá nhiều chuỗi nội bộ trong một số trường hợp.)

Kiểm tra các liên kết sau để biết thêm (hai chữ in đậm có sơ đồ đẹp để minh họa một phần vấn đề).

+1

Eddie - không phải là lý do để giữ các lớp mặc dù vì đã có các đối tượng hiện có gõ xung quanh với định nghĩa lớp "cũ"? –

+0

@Edie: Hầu hết các liên kết có liên quan đến bộ nhớ chứ không phải là kiến ​​trúc bộ nạp lớp sẽ hữu ích nhất để hiểu tại sao triển khai nóng là một vấn đề. Vấn đề bộ nhớ là hậu quả của các giải pháp. – OscarRyz

+0

Chỉ cần lưu ý về các chủ đề đang chạy: khi tôi để lại một luồng để ngủ qua việc triển khai lại do nhầm lẫn, thời điểm thread đánh thức nó đã ném một NoClassDefFound (các lớp không được triển khai), và sau đó (tất nhiên) đã chết. Sau cái chết của nó PermGen nên được OK để được GCed. –

6

Vấn đề một cách chung chung là mô hình bảo mật của Java mà thực sự cố gắng để ngăn chặn điều đó một lớp học có đã được tải được tải lại.

Tất nhiên Java từ khi bắt đầu đã hỗ trợ tải lớp động, điều khó khăn là tải lại lớp.

Nó được coi là có hại (và vì một lý do chính đáng) mà một ứng dụng java đang chạy đã được tiêm một lớp mới với mã độc. Ví dụ một java.lang.String nứt thực hiện bắt đầu từ internet, mà thay vì tạo chuỗi, xóa một số tập tin ngẫu nhiên hile gọi chiều dài phương thức(). Vì vậy, chúng theo cách Java đã được hình thành (và tôi giả sử .NET CLR do hậu quả, bởi vì nó được đánh giá cao "cảm hứng" trong JVM) là để ngăn chặn một lớp đã nạp để tải lại cùng một máy ảo đó.

Họ cung cấp cơ chế ghi đè "tính năng" này. Các trình nạp lớp, nhưng một lần nữa các quy tắc cho các trình nạp lớp là, họ nên yêu cầu cấp quyền cho trình nạp lớp "cha mẹ" trước khi cố nạp một lớp mới, nếu cha mẹ đã nạp lớp, lớp mới bị bỏ qua.

Ví dụ tôi đã sử dụng classloaders rằng tải một lớp từ LDAP hoặc RDBMS

Các triển khai nóng trở thành một điều cần thiết trong thế giới Java khi máy chủ ứng dụng đã trở thành chủ đạo cho Java EE (và cũng có thể tạo ra sự cần thiết của container vi như mùa xuân để tránh những loại gánh nặng này).

Khởi động lại toàn bộ máy chủ ứng dụng sau mỗi lần biên dịch mọi người đều phát điên.

Nhà cung cấp máy chủ ứng dụng, cung cấp bộ tải lớp "tùy chỉnh" này để giúp triển khai nóng và sử dụng tệp cấu hình, hành vi đó, NÊN bị tắt khi được đặt trong quá trình sản xuất. Nhưng sự cân bằng là bạn phải sử dụng tấn bộ nhớ trong phát triển. Vì vậy, cách tốt để làm điều này là khởi động lại mỗi 3 - 4 triển khai.

Điều này không xảy ra với các ngôn ngữ khác được thiết kế ngay từ đầu để tải các lớp học của họ.

Trong Ruby chẳng hạn, bạn thậm chí có thể thêm các phương thức vào một lớp đang chạy, ghi đè lên một phương thức lúc chạy hoặc thậm chí thêm một phương thức duy nhất vào một đối tượng cụ thể duy nhất.

Sự cân bằng trong các loại môi trường này là bộ nhớ và tốc độ của khóa học.

Tôi hy vọng điều này sẽ hữu ích.

EDIT

tôi đã tìm thấy sản phẩm này một thời gian trước đó hứa hẹn rằng tải lại là làm như đơn giản càng tốt. Tôi không nhớ liên kết khi lần đầu tiên tôi viết câu trả lời này, và tôi làm.

Đó là JavaRebel from ZeroTurnaround

+0

những gì một mớ hỗn độn trong đầu của bạn, eh? vì vậy java không được thiết kế để tải các lớp trong thời gian chạy, uh? đó là lý do tại sao nó có Class.forName() ngay từ đầu? –

+0

Đó không phải là những gì Oscar đã viết Vladimir. Ông viết rằng Java không được thiết kế để thay thế các lớp trong thời gian chạy. –

+0

ClassLoader lớp là có từ phiên bản 1. Không được thiết kế để thay thế các lớp học trong thời gian chạy? Bên cạnh đó, bảo mật không liên quan gì đến PermGen OOM. Giới hạn trên PermGen chỉ là một chi tiết thực hiện khó chịu của Sun JVM. –

3

Sun JVM đã PermGen không gian cố định, và cuối cùng đó là tất cả tiêu thụ (có, rõ ràng là do một lỗi trong mã classloader liên quan đến) => oom.

Nếu bạn có thể sử dụng JVM của nhà cung cấp khác (ví dụ: Weblogic), nó tự động mở rộng không gian PermGen, vì vậy bạn sẽ không bao giờ nhận được OOM liên quan đến permgen.

+0

Đợi ở đó. Điều này không đúng 100%. Vấn đề đá quý perm không chỉ liên quan đến JVM và các thư viện của nó. Ứng dụng xấu cũng có thể gây ra lỗi PermGen. Ngoài ra, tự động mở rộng PerGen nó hoàn toàn khác với việc giải quyết nó. Chờ đủ thời gian trong bất kỳ máy ảo nào và vấn đề sẽ xảy ra. – Antonio

+1

Đúng. Tuy nhiên, PermGen động cho phép bạn tồn tại nhiều lần hơn các triển khai nóng. Trong Sun JVM nó thường xảy ra trên redeployment # 1: -E –

0

Bạn đang sử dụng phiên bản java nào? Đã có lỗi trong Sun 1.4.2 đầu, nhưng nó đã được làm việc trong một thời gian dài.
BTW, bạn sẽ chia sẻ tin tức đó với nhóm của mình như thế nào? Bạn là đội trưởng?

+0

Nó thực sự là vấn đề mới kể từ 1.5. Hoặc ít nhất tôi chạy vào nó chỉ trong 1,5. –

+0

Chúng tôi đang gặp sự cố này với 1.6. Tôi có thể sẽ "chia nhỏ điều này" với nhóm của mình bằng cách gửi cho anh ấy một liên kết đến trang này :-P –