2014-10-08 15 views
11

Chúng tôi có một ứng dụng tạo ra các JVM mới và thực hiện mã thay mặt cho người dùng của chúng tôi. Đôi khi những người hết bộ nhớ, và trong trường hợp đó hành xử theo những cách rất khác nhau. Đôi khi họ ném ra một OutOfMemoryError, đôi khi họ đóng băng. Tôi có thể phát hiện sau này bằng một sợi nền rất nhẹ dừng lại để gửi tín hiệu nhịp tim khi chạy thấp trên bộ nhớ. Trong trường hợp đó, chúng tôi giết JVM, nhưng chúng tôi không bao giờ có thể hoàn toàn chắc chắn lý do thực sự cho việc không nhận được nhịp tim là gì. (Nó cũng có thể là vấn đề về mạng hoặc lỗi phân đoạn.)Cách tốt nhất để xử lý các điều kiện bộ nhớ trong Java là gì?

Cách tốt nhất để phát hiện đáng tin cậy các điều kiện bộ nhớ trong JVM là gì?

  • Về lý thuyết, các -XX: OnOutOfMemoryError tùy chọn sẽ hứa hẹn, nhưng nó là một cách hiệu quả không sử dụng được do lỗi này: https://bugs.openjdk.java.net/browse/JDK-8027434

  • Bắt một OutOfMemoryError là thực sự không phải là một lựa chọn tốt vì các lý do nổi tiếng (ví dụ như bạn không bao giờ biết nó xảy ra ở đâu), mặc dù nó hoạt động trong nhiều trường hợp.

  • Các trường hợp còn lại là những trường hợp JVM bị đóng băng và không ném lỗi OutOfMemoryError. Tôi vẫn chắc chắn rằng bộ nhớ lý do cho vấn đề này.

Có giải pháp thay thế hay giải pháp nào khác không? Thiết lập bộ sưu tập rác để làm cho JVM kết thúc chính nó thay vì đóng băng?

EDIT: Tôi có toàn quyền kiểm soát cả công cụ forking và JVM được chia rẽ cũng như mã đang được thực hiện bên trong, cả hai đều chạy trên Linux và sử dụng các tiện ích cụ thể của OS nếu điều đó có ích.

+2

Có vẻ như những gì bạn thực sự quan tâm là phát hiện khi một bộ nhớ ngoài đã xảy ra trong * một quy trình khác *; một điểm quan trọng mà thậm chí không bị ám chỉ bởi tiêu đề của câu hỏi của bạn. – supercat

+0

Cảm ơn. Tôi đã cố gắng làm rõ hơn trong bài đăng, nhưng tôi đã không thay đổi tiêu đề cho đến nay, vì tất cả các tiêu đề thay thế mà tôi có thể đưa ra đều gây hiểu lầm. Đặc biệt, tôi không quan tâm liệu chúng ta có lấy thông tin từ bên trong JVM hay không, từ JVM đang gọi, bằng cách xem nó như một JVM với hành vi cụ thể của nó, hoặc bằng cách chỉ xem nó như là một quá trình. –

+2

Nếu bạn không cải thiện tiêu đề của mình, nhiều người có thể trả lời thậm chí không thể mở bài đăng của bạn. Có lẽ "Kích hoạt một báo động nếu một quá trình Java VM hết bộ nhớ" sẽ là một tiêu đề tốt hơn? – supercat

Trả lời

1

sau khi thử nghiệm với điều này trong một thời gian khá lâu, đây là giải pháp mà làm việc cho chúng tôi:

  1. trong JVM sinh ra, bắt một lối ra OutOfMemoryError và ngay lập tức, báo hiệu sự ra khỏi tình trạng bộ nhớ với một mã exit để JVM điều khiển .
  2. Trong JVM được sinh ra, định kỳ kiểm tra lượng bộ nhớ đã tiêu thụ của Runtime hiện tại. Khi dung lượng bộ nhớ được sử dụng gần đến mức quan trọng, hãy tạo một tệp cờ báo hiệu tình trạng bộ nhớ ra khỏi bộ điều khiển JVM. Nếu chúng tôi khôi phục từ điều kiện này và thoát bình thường, hãy xóa tệp đó trước khi chúng tôi thoát.
  3. Sau khi JVM điều khiển tham gia vào JVM được chia rẽ, nó kiểm tra mã thoát được tạo ở bước (1) và tệp cờ được tạo trong bước (2). Thêm vào đó, nó kiểm tra xem tập tin hs_err_pidXXX.log có tồn tại không và chứa dòng "Out of Memory Error". (Tệp này được tạo bởi java trong trường hợp nó gặp sự cố.)

Chỉ sau khi thực hiện tất cả các kiểm tra này, chúng tôi có thể xử lý mọi trường hợp JVM được chia nhỏ hết bộ nhớ. Chúng tôi tin rằng kể từ đó, chúng tôi đã không bỏ lỡ một trường hợp điều này xảy ra.

Cờ java -XX:OnOutOfMemoryError không được sử dụng vì sự cố ngã ba, và -XX:+HeapDumpOnOutOfMemoryError không được sử dụng vì một vùng lưu trữ nhiều hơn chúng ta cần.

Giải pháp chắc chắn không phải là đoạn mã thanh lịch nhất từng được viết, mà là công việc của chúng tôi.

0

Trong trường hợp bạn có quyền kiểm soát cả ứng dụng và cấu hình, giải pháp tốt nhất là tìm nguyên nhân cơ bản cho lỗi OutOfMemoryError và sửa lỗi này, thay vì cố gắng che giấu các triệu chứng bằng cách bắt lỗi hoặc khởi động lại JVM. Từ những gì bạn mô tả, có vẻ như ứng dụng đang chạy trên JVM bị rò rỉ bộ nhớ, chỉ chạy bằng các tài nguyên không được cấp phép (bộ nhớ trong trường hợp của bạn) hoặc thỉnh thoảng xử lý các giao dịch đòi hỏi khối lượng lớn bất thường. Giải pháp cho những trường hợp này sẽ khác nhau:

  1. Trong trường hợp của một rò rỉ bộ nhớ, tìm ra nguyên nhân tiềm ẩn và có các kỹ sư sửa chữa nó. Các công cụ cho việc này bao gồm máy phân tích kết xuất đống, trình thu thập thông tin hoặc thiết bị dò rò rỉ
  2. Trong trường hợp tài nguyên không được cấp phép, bạn cần theo dõi mức tiêu thụ bộ nhớ ứng dụng, ví dụ như nhật ký thu gom rác và điều chỉnh kích thước của các bộ nhớ khác nhau dựa trên những gì bạn phải đối mặt .
  3. Trong trường hợp phân bổ đột biến trong quá trình giao dịch người dùng, bạn cần phải theo dõi mã khiến nó tăng tốc và nhờ các kỹ sư sửa lỗi - bằng cách vô hiệu hóa một số đầu vào người dùng nhất định hoặc tải và xử lý dữ liệu theo lô nhỏ hơn. Một trong hai chuỗi luồng hoặc đống kết quả từ các quy trình có thể hướng dẫn bạn hướng tới giải pháp.
+0

Từ câu hỏi tôi cho rằng không có nguyên nhân nào cho OutOfMemory, chỉ cần mã người dùng tùy ý đang chạy. Tôi không nghĩ anh ấy hỏi cách sửa mã của người dùng –

+0

Đúng vậy. Mọi người gửi quy trình công việc (coi đó là lập trình trực quan) cho chúng tôi. Chúng tôi chạy chúng trong các JVM được chia nhỏ. Nếu một luồng công việc như vậy thực hiện một phép toán ma trận trên một tệp dữ liệu 16 GB trong khi chọn làm như vậy trên một máy 8 GB, điều này không thể làm việc. Người gọi phải sửa chữa nó, nhưng chúng ta cần phải nói với họ rằng bộ nhớ là vấn đề, không phải lỗi JVM hoặc lỗi khác trên đầu của chúng tôi. –

+0

Trong trường hợp này, Plumbr (https://plumbr.eu) sẽ làm điều đó - trong trường hợp rò rỉ bộ nhớ, ví dụ bạn sẽ có được nguyên nhân gốc chính xác mà bạn có thể gửi đến kỹ sư dựa trên đó ngay lập tức có thể phóng to vấn đề như các báo cáo sự cố từ Plumbr tham khảo lại dòng chính xác trong mã nguồn gây ra vấn đề. – Ivo

1

Lựa chọn duy nhất là (unfortunatelly) để chấm dứt VM càng sớm càng tốt.

Vì bạn có thể không thể thay đổi tất cả mã của mình để phát hiện lỗi và phản hồi.Nếu bạn không tin tưởng vào OnOutOfMemoryError (Tôi tự hỏi tại sao nó không nên sử dụng vfork được sử dụng bởi Java 8, và nó hoạt động trên Windows), bạn ít nhất có thể kích hoạt một heapdump và giám sát từ bên ngoài đối với những tập tin:

java .... -XX:+HeapDumpOnOutOfMemoryError "-XX:OnOutOfMemeoryError=kill %p" 
+0

-XX: + HeapDumpOnOutOfMemoryError thực tế là một tùy chọn mà chúng tôi chưa thử. Tính đến nay, có vẻ như điều này cũng không được tạo ra một cách đáng tin cậy. WRT -XX: OnOutOfMemoryError: Nó hoạt động, nhưng chỉ khi hệ điều hành có ~ 50% tổng bộ nhớ vẫn còn, trong trường hợp tôi muốn đưa nó cho JVM hơn là đặt nó cho mục đích này chỉ :-) –

+0

@SimonFischer IMHO JRE chuyển sang vfork cho Runtime # exec(), tôi không chắc chắn nếu điều này cũng bao gồm OnOutOfMemory. Nhưng tất nhiên nó là chính xác rằng nó có thể không thể exexcute một lệnh trong điều kiện không gian hạn chế. Tôi không chắc chắn về máy chủ CrashReporter và hệ điều hành Creash báo cáo nếu họ là lựa chọn. Sau khi tất cả các bạn sẽ cần phải kiểm tra cho một quá trình biến mất anyway. – eckes

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