2012-04-11 39 views
26

Tôi vẫn đang học các dây giềng Java nên xin lỗi nếu có câu trả lời rõ ràng cho điều này. Tôi có một chương trình tốn rất nhiều bộ nhớ và tôi muốn tìm cách giảm thiểu việc sử dụng nó, nhưng sau khi đọc nhiều câu hỏi SO, tôi có ý tưởng rằng tôi cần chứng minh vấn đề ở đâu trước khi tôi bắt đầu tối ưu hóa nó. Vì vậy, đây là những gì tôi đã làm, tôi đã thêm một điểm break vào đầu chương trình của tôi và chạy nó, sau đó tôi bắt đầu visualVM và có hồ sơ bộ nhớ (tôi cũng đã làm điều tương tự trong netbeans chỉ để so sánh kết quả và họ giống nhau). Vấn đề của tôi là tôi không biết cách đọc chúng, tôi có diện tích cao nhất chỉ cần nói char[] và tôi không thể nhìn thấy bất kỳ mã nào hay bất kỳ thứ gì (có ý nghĩa bởi vì visualvm đang kết nối với jvm và không thể thấy nguồn của tôi, nhưng netbeans cũng không hiển thị cho tôi nguồn như nó khi làm hồ sơ cpu). Về cơ bản những gì tôi muốn biết là biến nào (và hy vọng nhiều chi tiết hơn trong phương thức nào) tất cả bộ nhớ đang được sử dụng để tôi có thể tập trung vào làm việc ở đó. Có cách nào dễ dàng để thực hiện việc này không? Tôi ngay bây giờ tôi đang sử dụng eclipse và java để phát triển (và cài đặt visualVM và netbeans đặc biệt cho profiling nhưng am sẵn sàng để cài đặt bất cứ điều gì khác mà bạn cảm thấy được công việc này được thực hiện).Làm thế nào để cấu hình bộ nhớ trong Java?

EDIT: Lý tưởng nhất, tôi đang tìm kiếm thứ gì đó sẽ lấy tất cả các đối tượng của tôi và sắp xếp chúng theo kích thước (vì vậy tôi có thể biết cái nào đang chiếm bộ nhớ). Hiện tại nó trả về thông tin chung như string [] hoặc int [] nhưng tôi muốn biết đối tượng của nó đang đề cập đến để tôi có thể làm việc để tăng kích thước của nó.

+0

Tôi sử dụng một trình hồ sơ hiển thị cho tôi đối tượng cũng đang được phân bổ. Tôi không biết nếu VisualVM có thể làm điều này, nhưng nó rất hữu ích. Cái mà tôi sử dụng là YourKit, nhưng nó không miễn phí (nhưng bạn có thể lấy giấy phép eval) –

+0

@PeterLawrey Tôi thực sự đã đọc câu trả lời bạn đã viết trước đây, nơi bạn đã đề cập đến bộ nhớ của mình là lựa chọn đầu tiên của bạn vì lời khuyên của bạn luôn tuyệt vời) nhưng có một chút tốn kém và tôi chỉ đang học. –

+0

mà không sử dụng một cái gì đó mà cụ mã của bạn không có phân tích sẽ có thể chỉ ra ví dụ == những gì bạn gọi là một tài liệu tham khảo trong nguồn của bạn. –

Trả lời

22

Strings là vấn đề

Về cơ bản trong Java, tài liệu tham khảo String (điều mà sử dụng char[] đằng sau hậu trường) sẽ thống trị hầu hết kinh doanh bộ nhớ ứng dụng khôn ngoan. Cách chúng được tạo ra xác định lượng bộ nhớ mà chúng tiêu thụ trong JVM.

Chỉ vì chúng rất cơ bản đối với hầu hết các ứng dụng kinh doanh dưới dạng kiểu dữ liệu và chúng cũng là một trong những bộ nhớ đói nhất. Đây không chỉ là một điều Java, String các kiểu dữ liệu chiếm nhiều bộ nhớ trong nhiều ngôn ngữ và thư viện thời gian chạy, bởi vì ít nhất chúng chỉ là mảng 1 byte cho mỗi ký tự hoặc tệ hơn (Unicode) chúng là mảng nhiều byte cho mỗi ký tự.

Một lần khi profiling sử dụng CPU trên một ứng dụng web mà cũng đã có một sự phụ thuộc Oracle JDBC tôi phát hiện ra rằng StringBuffer.append() thống trị chu kỳ CPU của nhiều bậc độ lớn hơn tất cả các phương pháp khác gọi kết hợp, ít hơn nhiều bất kỳ cuộc gọi phương pháp nào khác. Trình điều khiển JDBC đã thực hiện rất nhiều thao tác String, loại bỏ giao dịch sử dụng PreparedStatements cho mọi thứ.

gì bạn lo lắng về bạn không thể kiểm soát, không trực tiếp nào

Những gì bạn nên tập trung vào những gì là trong kiểm soát của bạn, mà là đảm bảo bạn không giữ cho tài liệu tham khảo dài hơn bạn cần, và rằng bạn không sao chép mọi thứ một cách không cần thiết. Các thói quen thu gom rác trong Java được tối ưu hóa cao và nếu bạn học cách các thuật toán của chúng hoạt động, bạn có thể đảm bảo chương trình của bạn hoạt động theo cách tối ưu để các thuật toán đó hoạt động.

Java Heap Memory không thích bằng tay quản lý bộ nhớ bằng các ngôn ngữ khác, những quy tắc không áp dụng

gì được coi là bộ nhớ rò rỉ bằng ngôn ngữ khác không phải là điều tương tự/nguyên nhân gốc rễ là trong Java với hệ thống thu gom rác của nó.

Rất có thể trong bộ nhớ Java không bị tiêu thụ bởi một đối tượng uber duy nhất bị rò rỉ (tham chiếu lơ lửng trong các môi trường khác).

Đó là hầu hết rất nhiều khả năng phân bổ nhỏ hơn vì StringBuffer/StringBuilder đối tượng không có kích thước thích hợp trên instantantations đầu tiên và sau đó phải tự động phát triển các char[] mảng tổ chức tiếp theo append() cuộc gọi.

Các đối tượng trung gian này có thể được giữ lâu hơn dự kiến ​​bởi bộ thu gom rác vì phạm vi của chúng và rất nhiều thứ khác có thể thay đổi theo thời gian chạy.

VÍ DỤ: bộ thu gom rác thải có thể quyết định có ứng cử viên, nhưng vì nó cho rằng vẫn còn nhiều thời gian để tiết kiệm thời gian, và nó sẽ đợi cho đến khi áp lực bộ nhớ tăng lên.

Bộ thu gom rác thực sự tốt ngay bây giờ, nhưng nó không phải là ma thuật, nếu bạn đang làm những điều thoái hóa, nó sẽ làm cho nó không hoạt động tối ưu. Có rất nhiều tài liệu trên internet về cài đặt bộ thu gom rác cho tất cả các phiên bản của JVM.

Các đối tượng không được tham chiếu này có thể không đạt đến thời gian mà bộ thu gom rác cho rằng cần phải xóa chúng khỏi bộ nhớ, hoặc có thể tham chiếu đến chúng do một số đối tượng khác nắm giữ (List) mà bạn không nhận ra vẫn chỉ vào đối tượng đó. Đây là điều thường được gọi là rò rỉ trong Java, một lỗ hổng tham chiếu cụ thể hơn.

VÍ DỤ: Nếu bạn biết bạn cần phải xây dựng một 4K String sử dụng một StringBuilder tạo ra nó với new StringBuilder(4096); không phải là mặc định, mà là giống như 32 và ngay lập tức sẽ bắt đầu tạo rác có thể đại diện cho nhiều lần những gì bạn nghĩ đối tượng nên được kích thước khôn ngoan.

Bạn có thể khám phá xem có bao nhiêu loại đối tượng được khởi tạo với VisualVM, điều này sẽ cho bạn biết những gì bạn cần biết. Sẽ không có một ánh sáng nhấp nháy lớn chỉ vào một trường hợp duy nhất của một lớp duy nhất nói rằng, "Đây là người tiêu dùng bộ nhớ lớn!", Đó là trừ khi chỉ có một trường hợp của một số char[] mà bạn đang đọc một số tập tin lớn vào, và điều này là không thể, bởi vì nhiều lớp khác sử dụng char[] nội bộ; và sau đó bạn đã biết khá nhiều rồi.

Tôi không thấy bất kỳ đề cập đến OutOfMemoryError

Bạn có lẽ không có một vấn đề trong mã của bạn, hệ thống thu gom rác thải chỉ có thể không được bị đặt dưới áp lực đủ để kick vào và đối tượng deallocate rằng bạn nghĩ rằng nó nên được dọn dẹp. Điều gì bạn nghĩ rằng là một vấn đề có thể không phải là, không trừ khi chương trình của bạn bị lỗi với OutOfMemoryError.Đây không phải là C, C++, Objective-C hoặc bất kỳ ngôn ngữ/thời gian chạy bộ nhớ thủ công nào khác. Bạn không thể quyết định cái gì có trong bộ nhớ hay không ở mức độ chi tiết mà bạn mong đợi bạn sẽ có thể.

0

Nếu bạn sử dụng VisualVM để kiểm tra mức sử dụng bộ nhớ, nó sẽ tập trung vào dữ liệu chứ không phải phương pháp. Có lẽ dữ liệu char [] lớn của bạn là do nhiều giá trị String gây ra? Trừ khi bạn đang sử dụng đệ quy, dữ liệu sẽ không được từ các biến cục bộ. Vì vậy, bạn có thể tập trung vào các phương pháp chèn các phần tử vào các cấu trúc dữ liệu lớn. Để tìm hiểu những gì báo cáo chính xác gây ra "rò rỉ bộ nhớ" của bạn, tôi khuyên bạn nên bổ sung

2

Tôi khuyên bạn nên chụp các vùng heap và sử dụng công cụ như Eclipse MAT cho phép bạn phân tích chúng. Có rất nhiều tutorials có sẵn. Nó cung cấp một cái nhìn của dominator tree để cung cấp cái nhìn sâu sắc vào các mối quan hệ giữa các đối tượng trên heap. Cụ thể cho những gì bạn đã đề cập, tính năng "đường dẫn đến GC root" của MAT sẽ cho bạn biết phần lớn các đối tượng char [], String [] và int [] đang được tham chiếu. JVisualVM cũng có thể hữu ích trong việc xác định rò rỉ và phân bổ, đặc biệt là bằng cách sử dụng ảnh chụp nhanh với dấu vết ngăn xếp phân bổ. Có khá một vài số walk-throughs of the process để nhận các ảnh chụp nhanh và so sánh chúng để tìm điểm phân bổ.

7

Trong JProfiler, bạn có thể đi tới bộ tập hợp heap và kích hoạt chế độ xem đối tượng lớn nhất. Bạn sẽ thấy các đối tượng giữ lại nhiều bộ nhớ nhất. Bộ nhớ "giữ lại" là bộ nhớ sẽ được giải phóng bởi bộ thu gom rác nếu bạn đã xóa đối tượng.

Sau đó, bạn có thể mở các nút đối tượng để xem cây tham chiếu của các đối tượng được giữ lại. Dưới đây là một ảnh chụp màn hình về quan điểm đối tượng lớn nhất:

enter image description here

Disclaimer: Công ty của tôi phát triển JProfiler

1

Java JDK đi kèm với JVisualVM dưới thư mục bin, một khi máy chủ ứng dụng của bạn (ví dụ đang chạy) bạn có thể chạy visualvm và kết nối nó với localhost của bạn, sẽ cung cấp cho bạn phân bổ bộ nhớ và cho phép bạn thực hiện đống phân vùng

enter image description here

Để biết các bước chi tiết hơn về cách bật: http://sysdotoutdotprint.com/index.php/2017/08/01/turn-profiler-java/

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