Cách tìm rò rỉ bộ nhớ Java


142

Làm thế nào để bạn tìm thấy một rò rỉ bộ nhớ trong Java (ví dụ, sử dụng JHat)? Tôi đã cố gắng tải đống heap lên trong JHat để có cái nhìn cơ bản. Tuy nhiên, tôi không hiểu làm thế nào tôi có thể tìm thấy tài liệu tham khảo gốc ( ref ) hoặc bất cứ thứ gì nó được gọi. Về cơ bản, tôi có thể nói rằng có hàng trăm megabyte mục băm ([java.util.HashMap $ Entry hoặc một cái gì đó tương tự), nhưng bản đồ được sử dụng ở khắp mọi nơi ... Có cách nào để tìm kiếm bản đồ lớn , hoặc có lẽ tìm rễ chung của cây đối tượng lớn?

[Chỉnh sửa] Ok, tôi đã đọc câu trả lời cho đến nay nhưng hãy nói rằng tôi là một thằng khốn rẻ tiền (có nghĩa là tôi thích học cách sử dụng JHat hơn là trả tiền cho JProfiler). Ngoài ra, JHat luôn có sẵn vì nó là một phần của JDK. Tất nhiên trừ khi không có cách nào với JHat ngoài sức mạnh vũ phu, nhưng tôi không thể tin đó có thể là trường hợp.

Ngoài ra, tôi không nghĩ rằng tôi sẽ có thể thực sự sửa đổi (thêm ghi nhật ký của tất cả các kích thước bản đồ) và chạy nó đủ lâu để tôi nhận thấy sự rò rỉ.


Đây là một "phiếu bầu" khác cho JProfiler. Nó hoạt động khá tốt để phân tích heap, có giao diện người dùng tốt và hoạt động khá tốt. Như McKenzieG1 nói, 500 đô la rẻ hơn so với thời gian bạn sẽ đốt để tìm kiếm nguồn cho những rò rỉ này. Theo như giá của các công cụ, nó không phải là xấu.
joev

Câu trả lời:


126

Tôi sử dụng cách tiếp cận sau để tìm rò rỉ bộ nhớ trong Java. Tôi đã sử dụng jProfiler rất thành công, nhưng tôi tin rằng bất kỳ công cụ chuyên dụng nào có khả năng vẽ đồ thị (khác biệt sẽ dễ dàng phân tích hơn ở dạng đồ họa) sẽ hoạt động.

  1. Khởi động ứng dụng và đợi cho đến khi nó về trạng thái "ổn định", khi tất cả quá trình khởi tạo hoàn tất và ứng dụng không hoạt động.
  2. Chạy hoạt động bị nghi ngờ tạo ra rò rỉ bộ nhớ nhiều lần để cho phép bất kỳ bộ đệm, khởi tạo liên quan đến DB nào diễn ra.
  3. Chạy GC và chụp ảnh bộ nhớ.
  4. Chạy lại hoạt động. Tùy thuộc vào mức độ phức tạp của hoạt động và kích thước của dữ liệu được xử lý hoạt động có thể cần phải được chạy nhiều đến nhiều lần.
  5. Chạy GC và chụp ảnh bộ nhớ.
  6. Chạy một diff cho 2 snapshot và phân tích nó.

Về cơ bản phân tích nên bắt đầu từ khác biệt tích cực lớn nhất bằng cách nói, các loại đối tượng và tìm ra nguyên nhân khiến các đối tượng phụ đó dính vào bộ nhớ.

Đối với các ứng dụng web xử lý các yêu cầu trong một số phân tích luồng trở nên phức tạp hơn, tuy nhiên cách tiếp cận chung vẫn được áp dụng.

Tôi đã thực hiện khá nhiều dự án đặc biệt nhằm mục đích giảm dung lượng bộ nhớ của các ứng dụng và cách tiếp cận chung này với một số điều chỉnh và thủ thuật cụ thể của ứng dụng luôn hoạt động tốt.


7
Hầu hết (nếu không phải tất cả) các trình biên dịch Java cung cấp cho bạn một tùy chọn để gọi GC chỉ bằng một nút bấm. Hoặc bạn có thể gọi System.gc () từ vị trí thích hợp trong mã của bạn.
Dima Malenko

3
Ngay cả khi chúng ta gọi System.gc () JVM có thể chọn bỏ qua cuộc gọi. AFAIK đây là cụ thể của JVM. +1 cho câu trả lời.
Aniket Thakur 27/12/13

4
Chính xác thì "ảnh chụp nhanh bộ nhớ là gì?" Có cái gì đó sẽ cho tôi biết số lượng của từng loại đối tượng mà mã của tôi đang chạy không?
viết

2
Làm thế nào để tôi đi từ "bắt đầu từ sự khác biệt tích cực lớn nhất theo loại đối tượng" đến "tìm ra nguyên nhân khiến những đối tượng phụ đó dính vào bộ nhớ"? Tôi thấy những thứ rất chung chung như int [], Object [], String, v.v ... Làm thế nào để tôi tìm thấy chúng đến từ đâu?
Vituel

48

Người hỏi ở đây, tôi phải nói rằng việc có được một công cụ không mất 5 phút để trả lời bất kỳ nhấp chuột nào giúp việc tìm kiếm rò rỉ bộ nhớ tiềm năng dễ dàng hơn rất nhiều.

Vì mọi người đang đề xuất một số công cụ (tôi chỉ thử trực quan wm vì tôi đã nhận được điều đó trong bản dùng thử JDK và JPcoat) Tôi mặc dù tôi nên đề xuất một công cụ nguồn mở / miễn phí được xây dựng trên nền tảng Eclipse, Trình phân tích bộ nhớ (đôi khi được gọi là bộ nhớ SAP máy phân tích) có sẵn trên http://www.eclipse.org/mat/ .

Điều thực sự thú vị về công cụ này là nó đã lập chỉ mục cho vùng heap khi tôi mở lần đầu tiên cho phép nó hiển thị dữ liệu như heap giữ lại mà không phải chờ 5 phút cho mỗi đối tượng (gần như tất cả các hoạt động đều nhanh hơn so với các công cụ khác mà tôi đã thử) .

Khi bạn mở bãi chứa, màn hình đầu tiên hiển thị cho bạn một biểu đồ hình tròn với các đối tượng lớn nhất (đếm heap giữ lại) và người ta có thể nhanh chóng điều hướng xuống các đối tượng lớn để thoải mái. Nó cũng có một nghi phạm rò rỉ có khả năng mà tôi nhận thấy có thể có ích, nhưng vì điều hướng là đủ cho tôi nên tôi không thực sự hiểu về nó.


1
Đáng chú ý: rõ ràng trong Java 5 trở lên, HeapDumpOnCtrlBreaktham số VM không khả dụng . Giải pháp tôi đã tìm thấy (cho đến nay, vẫn đang tìm kiếm) là sử dụng JMap để kết xuất .hproftệp, sau đó tôi đưa vào Eclipse và sử dụng MAT để kiểm tra.
Ben

1
Liên quan đến việc có được heap dump, hầu hết các trình biên dịch (bao gồm cả JVisualVM) bao gồm tùy chọn để kết xuất cả heap và luồng vào một tệp.
bbaja42

13

Một công cụ là một trợ giúp lớn.

Tuy nhiên, có những lúc bạn không thể sử dụng một công cụ: đống heap quá lớn khiến nó bị hỏng công cụ, bạn đang cố gắng khắc phục sự cố máy trong một số môi trường sản xuất mà bạn chỉ có quyền truy cập shell, v.v.

Trong trường hợp đó, sẽ giúp biết cách của bạn xung quanh tệp kết xuất hprof.

Tìm kiếm BẮT ĐẦU BẮT ĐẦU. Điều này cho bạn thấy những đối tượng đang sử dụng bộ nhớ nhiều nhất. Nhưng các đối tượng không được gộp lại với nhau theo loại: mỗi mục cũng bao gồm ID "theo dõi". Sau đó, bạn có thể tìm kiếm "TRACE nnnn" đó để xem một vài khung hình trên cùng của ngăn xếp nơi đối tượng được phân bổ. Thông thường, một khi tôi thấy nơi đối tượng được phân bổ, tôi tìm thấy một lỗi và tôi đã hoàn thành. Ngoài ra, lưu ý rằng bạn có thể kiểm soát số lượng khung hình được ghi trong ngăn xếp với các tùy chọn thành -Xrunhprof.

Nếu bạn kiểm tra trang web phân bổ và không thấy có gì sai, bạn phải bắt đầu xâu chuỗi ngược từ một số đối tượng sống đó sang đối tượng gốc, để tìm chuỗi tham chiếu bất ngờ. Đây là nơi một công cụ thực sự hữu ích, nhưng bạn có thể làm điều tương tự bằng tay (tốt, với grep). Không chỉ có một đối tượng gốc (tức là đối tượng không chịu sự thu gom rác). Chủ đề, lớp và khung ngăn xếp đóng vai trò là đối tượng gốc và bất kỳ thứ gì chúng tham chiếu mạnh đều không thể thu thập được.

Để thực hiện xâu chuỗi, hãy xem trong phần HEAP DUMP để biết các mục có id dấu vết xấu. Điều này sẽ đưa bạn đến một mục OBJ hoặc ARR, hiển thị một định danh đối tượng duy nhất ở dạng thập lục phân. Tìm kiếm tất cả các lần xuất hiện của id đó để tìm ai có tham chiếu mạnh đến đối tượng. Theo từng con đường phía sau khi chúng phân nhánh cho đến khi bạn tìm ra nơi rò rỉ. Xem tại sao một công cụ rất tiện dụng?

Các thành viên tĩnh là một vi phạm lặp lại cho rò rỉ bộ nhớ. Trên thực tế, ngay cả khi không có công cụ, cũng đáng để dành vài phút xem qua mã của bạn cho các thành viên Bản đồ tĩnh. Một bản đồ có thể phát triển lớn? Có bất cứ điều gì bao giờ làm sạch các mục của nó?


Sự cố đống heap rất lớn, nó đã phá vỡ công cụ mà tôi đã kiểm tra, jhatMATdường như cố gắng tải toàn bộ đống heap vào bộ nhớ, và do đó thường gặp sự cố với một OutOfMemoryErrorbãi rác lớn (ví dụ, từ các ứng dụng phân tích heap cần thiết nhất! ). NetBeans Profiler dường như sử dụng một thuật toán khác để lập chỉ mục các tham chiếu có thể bị chậm trên các bãi lớn nhưng ít nhất không tiêu thụ bộ nhớ không giới hạn trong công cụ và gặp sự cố.
Jesse Glick

10

Hầu hết thời gian, trong các ứng dụng doanh nghiệp, heap Java đưa ra lớn hơn kích thước lý tưởng tối đa 12 đến 16 GB. Tôi đã gặp khó khăn để làm cho trình hồ sơ NetBeans hoạt động trực tiếp trên các ứng dụng java lớn này.

Nhưng thường thì điều này là không cần thiết. Bạn có thể sử dụng tiện ích jmap đi kèm với jdk để nhận một đống heap "trực tiếp", đó là jmap sẽ kết xuất heap sau khi chạy GC. Thực hiện một số thao tác trên ứng dụng, đợi cho đến khi hoàn thành thao tác, sau đó lấy một đống heap "sống" khác. Sử dụng các công cụ như MAT MAT để tải các heapdumps, sắp xếp trên biểu đồ, xem các đối tượng nào đã tăng hoặc mức nào cao nhất, Điều này sẽ cho một manh mối.

su  proceeuser
/bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process)

Chỉ có một vấn đề với cách tiếp cận này; Các đống lớn, ngay cả với tùy chọn trực tiếp, có thể quá lớn để chuyển ra vòng phát triển và có thể cần một máy có đủ bộ nhớ / RAM để mở.

Đó là nơi biểu đồ lớp đi vào hình ảnh. Bạn có thể kết xuất biểu đồ lớp trực tiếp bằng công cụ jmap. Điều này sẽ chỉ cung cấp biểu đồ lớp sử dụng bộ nhớ. Về cơ bản, nó sẽ không có thông tin để xâu chuỗi tham chiếu. Ví dụ, nó có thể đặt mảng char ở trên cùng. Và lớp String ở đâu đó bên dưới. Bạn phải tự vẽ kết nối.

jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt

Thay vì lấy hai đống, hãy lấy hai biểu đồ lớp, như mô tả ở trên; Sau đó so sánh biểu đồ lớp và xem các lớp đang tăng lên. Xem nếu bạn có thể liên kết các lớp Java với các lớp ứng dụng của bạn. Điều này sẽ cho một gợi ý khá tốt. Dưới đây là một kịch bản pythons có thể giúp bạn so sánh hai bãi chứa biểu đồ jmap. histogramparser.py

Cuối cùng, các công cụ như JConolse và VisualVm rất cần thiết để xem sự tăng trưởng bộ nhớ theo thời gian và xem có bị rò rỉ bộ nhớ hay không. Cuối cùng, đôi khi vấn đề của bạn có thể không phải là rò rỉ bộ nhớ, mà là việc sử dụng bộ nhớ cao. Để điều này cho phép ghi nhật ký GC, sử dụng một công cụ nén mới tiên tiến hơn và mới hơn như G1GC; và bạn có thể sử dụng các công cụ jdk như jstat để xem hành vi của GC trực tiếp

jstat -gccause pid <optional time interval>

Các tài liệu tham khảo khác cho google cho -jhat, jmap, GC đầy đủ, phân bổ hài hước, G1GC


1
đã thêm một bài đăng blog với nhiều chi tiết hơn ở đây - alexpunnen.blogspot.in/2015/06/ Kẻ
Alex Punnen

5

Có những công cụ sẽ giúp bạn tìm ra sự rò rỉ của mình, như JPcoat, YourKit, AD4J hoặc JRockit Mission Control. Cái cuối cùng là cái mà cá nhân tôi biết rõ nhất. Bất kỳ công cụ tốt nào cũng sẽ cho phép bạn đi sâu vào một mức độ mà bạn có thể dễ dàng xác định những gì rò rỉ và nơi các đối tượng rò rỉ được phân bổ.

Sử dụng HashTables, Hashmaps hoặc tương tự là một trong vài cách mà bạn hoàn toàn có thể rò rỉ bộ nhớ trong Java. Nếu tôi phải tìm ra rò rỉ bằng tay, tôi sẽ in một cách ngẫu nhiên kích thước của HashMaps của tôi và từ đó tìm ra nơi tôi thêm các mục và quên xóa chúng.


4

Chà, luôn có giải pháp công nghệ thấp là thêm ghi nhật ký kích thước bản đồ của bạn khi bạn sửa đổi chúng, sau đó tìm kiếm nhật ký mà bản đồ đang phát triển vượt quá kích thước hợp lý.



0

Bạn thực sự cần phải sử dụng một trình lược tả bộ nhớ theo dõi phân bổ. Hãy xem JProfiler - tính năng "heap walker" của họ rất tuyệt và họ đã tích hợp với tất cả các IDE Java chính. Nó không miễn phí, nhưng nó cũng không đắt lắm ($ 499 cho một giấy phép) - bạn sẽ đốt cháy thời gian trị giá $ 500 khá nhanh chóng phải vật lộn để tìm ra một rò rỉ với các công cụ ít phức tạp hơn.


0

Bạn có thể tìm hiểu bằng cách đo kích thước sử dụng bộ nhớ sau khi gọi bộ thu gom rác nhiều lần:

Runtime runtime = Runtime.getRuntime();

while(true) {
    ...
    if(System.currentTimeMillis() % 4000 == 0){
        System.gc();
        float usage = (float) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024;
        System.out.println("Used memory: " + usage + "Mb");
    }

}

Nếu các số đầu ra bằng nhau, không có rò rỉ bộ nhớ trong ứng dụng của bạn, nhưng nếu bạn thấy sự khác biệt giữa số lượng sử dụng bộ nhớ (số tăng), thì có rò rỉ bộ nhớ trong dự án của bạn. Ví dụ:

Used memory: 14.603279Mb
Used memory: 14.737213Mb
Used memory: 14.772224Mb
Used memory: 14.802681Mb
Used memory: 14.840599Mb
Used memory: 14.900841Mb
Used memory: 14.942261Mb
Used memory: 14.976143Mb

Lưu ý rằng đôi khi phải mất một thời gian để giải phóng bộ nhớ bằng một số hành động như luồng và ổ cắm. Bạn không nên đánh giá bằng các đầu ra đầu tiên, Bạn nên kiểm tra nó trong một khoảng thời gian cụ thể.


0

Kiểm tra màn hình này về việc tìm kiếm rò rỉ bộ nhớ với JProfiler. Đó là lời giải thích trực quan của @Dima Malenko Trả lời.

Lưu ý: Mặc dù JProfiler không phải là phần mềm miễn phí, nhưng phiên bản dùng thử có thể xử lý tình huống hiện tại.


0

Vì hầu hết chúng ta đều sử dụng Eclipse để viết mã, Tại sao không sử dụng Công cụ phân tích bộ nhớ (MAT) trong Eclipse. Nó hoạt động rất tốt.

Các Eclipse MAT là một tập hợp của các plug-in cho Eclipse IDE cung cấp các công cụ để phân tích heap dumpstừ ứng dụng Java và xác định memory problemstrong ứng dụng.

Điều này giúp nhà phát triển tìm ra rò rỉ bộ nhớ với các tính năng sau

  1. Có được ảnh chụp nhanh bộ nhớ (Heap Dump)
  2. Biểu đồ
  3. Heap giữ lại
  4. Cây thống lĩnh
  5. Khám phá các đường dẫn đến gốc rễ GC
  6. Thanh tra
  7. Bộ nhớ chung chống mẫu
  8. Ngôn ngữ truy vấn đối tượng

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.