2012-03-02 10 views
6

Tôi có một ứng dụng hoạt động mạnh với nhiều đối tượng tùy chỉnh được tạo bên trong các phương thức và không bao giờ cần bên ngoài chúng. Toàn bộ cấu trúc là (theo ý kiến ​​của tôi) rất tốt đối tượng theo định hướng và sử dụng dịch vụ, tiện ích và một DI-Model.Cách cải thiện ứng dụng để tránh các vấn đề về không gian heap

Bây giờ, khi tôi chạy thử nghiệm "lớn" đầu tiên của mình, tôi nhanh chóng gặp phải Ngoại lệ OutOfMemory. Bây giờ, tôi không chỉ muốn tăng không gian heap và được thực hiện với nó, như tôi có thể tưởng tượng rằng sẽ không giải quyết vấn đề nhưng thay vì trì hoãn nó cho đến khi ứng dụng của tôi đã phát triển hơn và gặp phải vấn đề tương tự sau đó.

Tôi đang tìm một số giải pháp đơn giản và dễ thực hiện, mẹo và đoạn mã giúp ứng dụng xử lý bộ sưu tập rác và không gian heap, đặc biệt khi nói đến nhiều vòng hoạt động với tạo đối tượng.

Giống như "không tạo đối tượng trong vòng lặp, hãy tạo chúng trước vòng lặp và ghi đè lên trong" và các loại.

+0

Tôi nghĩ rằng nếu bạn có thể đăng một số phương pháp của bạn (ít nhất cấu trúc) nó sẽ dễ dàng hơn nhiều. xem thêm http://stackoverflow.com/q/627784/1163434 – gawicks

Trả lời

2

Tôi sẽ bắt đầu bằng cách lược tả ứng dụng của bạn và tìm kiếm các điểm nóng bộ nhớ bằng cách sử dụng jvisualvm (một phần của JDK). Điều này sẽ cung cấp cho bạn và chỉ ra các đối tượng của bạn lớn như thế nào và các cuộc gọi phương thức nào dẫn đến việc sử dụng bộ nhớ cao. Nó cũng sẽ cho bạn biết bao lâu các đối tượng của bạn là haning xung quanh trong bộ nhớ mà nói chung là một điểm khởi đầu tốt như bạn muốn giảm phạm vi để được càng ngắn càng tốt.

Bước tiếp theo là xác định sự tương đồng trong đối tượng của bạn bằng cách tinh chỉnh thiết kế của bạn hoặc bằng cách triển khai bộ nhớ cache. Nếu bạn đang tải dữ liệu từ một kho lưu trữ cố định thì bạn có thể sử dụng softreferences để JVM chạy hết đống các đối tượng này sẽ được GCed (nếu bạn thay đổi các đối tượng này, bạn sẽ cần phải tồn tại chúng trước khi loại bỏ tham chiếu cứng). Sau đó, nếu họ là cần thiết một lần nữa ứng dụng của bạn sẽ chỉ cần tải lại chúng từ các cửa hàng sao lưu (DB, tập tin hoặc bất cứ điều gì).

Hãy chắc chắn rằng bạn biết GC công trình như thế nào và hiểu tài liệu tham khảo đối tượng của bạn:

  • mạnh/Direct
  • mềm
  • Yếu
  • Phantom

Dưới đây là một vài điều tốt giải thích các tham chiếu và GC:

http://www.java-tips.org/java-se-tips/java.util/using-weakhashmap-for-listener-lists.html

http://pawlan.com/monica/articles/refobjs/

http://www.kdgregory.com/index.php?page=java.refobj

+1

Cảm ơn bạn đã liên kết! Chương trình rất hữu ích. –

0

Nullify bất kỳ tham chiếu đối tượng nào ngay khi không cần một đối tượng nữa? Ngay sau khi một đối tượng không có nhiều tham chiếu đến nó, GC có thể thu thập nó, nhưng tôi đoán rằng bạn đã biết điều này. GC cũng có thể hoạt động trên biểu đồ đối tượng không được xử lý (nếu A có tham chiếu duy nhất đến B và không có tham chiếu đến A, thì A và B có thể được thu thập). Nó gần như là vô nghĩa khi gọi System.gc() vì nếu JVM cần thêm bộ nhớ, nó sẽ tự làm nó và sau đó sử dụng bộ nhớ giải phóng. Nếu nó không thể giải phóng thêm bộ nhớ nào thì bạn sẽ chạy vào OOME.

Bây giờ kích thước heap mặc định không phải là lớn và do đó, nó thường khá chấp nhận được để yêu cầu kích thước heap hơn.

Tạo đối tượng trong các vòng lặp không phải là một mẫu đặc biệt xấu và trong nhiều trường hợp, nó khá phù hợp. Điều cần tránh là lặp lại ngay lập tức cùng một đối tượng trong một vòng lặp. Thông thường, chuỗi nối nên tránh trong các vòng lặp và được thay thế bởi một StringBuilder được tạo ra bên ngoài vòng lặp, vì nó kém hiệu quả hơn nhiều về mặt hiệu năng nhưng không phải về mặt bộ nhớ.

Không chắc chắn tôi thực sự trả lời câu hỏi của bạn.

4

Phần quan trọng nhất của lời khuyên mà tôi có thể cung cấp cho bạn cũng giống như với bất kỳ vấn đề hiệu suất:

hồ sơ, cải thiện, lặp lại

Sử dụng một hồ sơ (ví dụ VisualVM) để tìm nơi lượng bộ nhớ lớn nhất được tiêu thụ. Cải thiện mã của bạn, trước tiên hãy xóa mọi rò rỉ bộ nhớ và sau đó giảm mức tiêu thụ bộ nhớ nói chung. Lặp lại quá trình này cho đến khi bạn hài lòng với chất lượng và hiệu suất của mã của bạn.

EDIT:

Một vài thủ đoạn của thương mại:

  • Share đối tượng thay vì sao chép, khi có thể.

  • Hãy coi chừng các lớp thu thập Java (ví dụ: các triển khai khác nhau Collection<T>Map<K,V>). Tùy thuộc vào những gì bạn đang lưu trữ và bộ sưu tập đang sử dụng là gì, bạn có thể dễ dàng increase your memory consumption by an order of magnitude mà không mong đợi nó.

  • Trong khi Java không (thông thường) có rò rỉ bộ nhớ theo nghĩa giống như gặp phải trong C, mã Java thường có vấn đề với các đối tượng được giữ lại quá ngày hết hạn của chúng.

    Để tránh điều này, hãy giới hạn phạm vi tài liệu tham khảo của bạn càng nhiều càng tốt hoặc đặt chúng thành null khi bạn hoàn thành với đối tượng đó. Lưu ý: đừng làm điều đó với việc thiết lập null, đặc biệt là trong các phương pháp tầm thường dự kiến ​​sẽ sớm quay trở lại.

    Quan trọng nhất: đảm bảo bạn xóa một đối tượng khỏi bất kỳ bộ sưu tập nào mà nó có thể đã được nhập khi bạn hoàn thành nó. Không làm như vậy là một công thức tốt cho một số OutOfMemoryError - và nguyên nhân phổ biến nhất của những gì mọi người gọi là rò rỉ bộ nhớ trong thế giới Java.

+0

+1: Tôi cũng sẽ xem xét một hồ sơ thương mại nếu VisualVM là không đủ. Tôi sử dụng YourKit. –

5

Một số điểm:

  • Không có gì sai lầm cơ bản với sự gia tăng không gian heap. Các ứng dụng khác nhau có xu hướng có các yêu cầu khác nhau.
  • Sử dụng hồ sơ để xem những gì thực sự đang diễn ra. Ví dụ ở đây bạn có thể tìm thấy phân tích đống: MAT
  • Khi bạn tìm ra rằng trường hợp của lớp nhất định phải chịu trách nhiệm về 80% lượng tiêu thụ đống:
    • cố gắng để tìm thấy bộ thường chia sẻ của các biến với cùng một giá trị. Đó là những ứng cử viên là một đối tượng có thể được chia sẻ bởi nhiều đối tượng.
    • Kiểm tra đặc biệt là bạn lưu trữ một số tham chiếu đến đồ thị đối tượng tương đối lớn với biến tồn tại lâu hơn vòng lặp của bạn (biến cục bộ tiêu thụ ngăn xếp).
    • Để tài liệu tham khảo rơi ra khỏi phạm vi nhanh nhất có thể.
    • Nếu bạn sử dụng các lớp bên trong, hãy kiểm tra các lớp không tĩnh, vì lớp bên trong không tĩnh giữ tham chiếu đến đối tượng chứa.
+0

Tôi thực sự thích 4 viên đạn phụ của bạn (+1) – DaveFar

+2

+1 để tham chiếu đến các lớp bên trong. Họ có khá nhiều cạm bẫy mà hầu hết mọi người đều không biết ... – thkala

0

Thứ nhất, tôi sẽ kiểm tra lại thiết kế của tôi, tập trung vào cần thiết trên phức tạp (Landau/Big O notation).

Thứ hai, tôi sẽ đọc Josh Bloch's Effective Java, Item 6 (Loại bỏ tham chiếu đối tượng lỗi thời), để có được một số gợi ý về

  • lý do thường xuyên cho "rò rỉ bộ nhớ"
  • thay vì sử dụng phạm vi nhỏ nhất có thể sau đó vô hiệu hóa không còn các đối tượng cần thiết
  • bộ nhớ đệm và các nhóm lưu trữ.

Thứ ba, nếu bạn vẫn có ngoại lệ OOM, tôi sẽ theo dõi Mikko's advises.