Các ứng dụng Java và Kotlin quản lý bộ nhớ thông qua một vùng nhớ khối lớn được thu gom rác. Khi các đối tượng không còn truy cập được nữa, trình thu gom rác (GC) cuối cùng sẽ thu hồi không gian của chúng. Rò rỉ bộ nhớ xảy ra khi các đối tượng không còn cần thiết vẫn được "các gốc GC" giữ lại, khiến chúng không được thu hồi.
Các khái niệm chính
GC roots
GC Root là một loại đối tượng đặc biệt mà trình thu gom rác luôn coi là có thể truy cập. Ví dụ:
- Các luồng đang hoạt động (và các đối tượng được tham chiếu từ các khung ngăn xếp Java hiện đang thực thi).
- Lớp có các phương thức đang chạy.
- JNI references (các lượt tham chiếu toàn cục hoặc cục bộ do mã gốc giữ lại).
Đường dẫn đến gốc GC
Miễn là có một chuỗi tham chiếu từ GC Root đến một đối tượng, đối tượng đó sẽ "có thể truy cập" và không thể thu gom rác. Chuỗi này được gọi là Đường dẫn đến gốc GC. Để khắc phục tình trạng rò rỉ bộ nhớ, bạn phải xác định và phá vỡ chuỗi này.

Cây chi phối
Mặc dù đường dẫn đến gốc GC cho bạn biết lý do một đối tượng vẫn còn tồn tại, nhưng đường dẫn này không cho bạn biết dung lượng bộ nhớ sẽ được thu hồi nếu tham chiếu đó bị hỏng. Để làm việc này, chúng ta sử dụng Dominator Trees (Cây thống trị).
Đối tượng A được cho là chi phối đối tượng B nếu mọi đường dẫn từ bất kỳ gốc GC nào đến B đều phải đi qua A. Nếu A chiếm ưu thế hơn B, thì việc khôi phục A cũng sẽ đảm bảo rằng B có thể được khôi phục, vì không có đường dẫn nào khác từ bất kỳ gốc nào đến B.
Sơ đồ sau đây minh hoạ một biểu đồ đối tượng và cây thống trị tương ứng. Lưu ý cách cả A và B đều truy cập vào đối tượng D trong biểu đồ, vì vậy, cả A và B đều không chiếm ưu thế hơn D; thay vào đó, GC Root là đối tượng chiếm ưu thế gần nhất.

Lấy tệp báo lỗi vùng nhớ Java
Tệp báo lỗi là ảnh chụp nhanh của tất cả các đối tượng trong vùng nhớ khối xếp Java tại một thời điểm cụ thể.
Sử dụng ADB
Để ghi lại một tệp báo lỗi từ một quy trình đang chạy, bạn có thể truyền trực tiếp tên gói đến am dumpheap. Để chạy lệnh này, bạn phải tạo ứng dụng bằng <profileable android:shell="true"/> hoặc <debuggable>.
# 1. Trigger the dump (the command takes a moment to complete):
adb shell am dumpheap -g -b png com.android.memorylab /data/local/tmp/heap.hprof
# 2. Pull the file to your development machine:
adb pull /data/local/tmp/heap.hprof .
Sử dụng Perfetto
Perfetto cũng có thể ghi lại các kết xuất heap Java trong quá trình theo dõi trên toàn hệ thống bằng cách bật nguồn dữ liệu android.java_hprof trong cấu hình Perfetto. Điều này hữu ích cho việc tương quan trạng thái heap với các sự kiện hệ thống khác.
Để ghi lại một tệp báo lỗi cho ứng dụng MemoryLab bằng Perfetto, bạn có thể sử dụng lệnh sau:
# Create a temp file for the configuration
cat > /tmp/java_heap.pbtx <<EOF
data_sources: {
config {
name: "android.java_hprof"
java_hprof_config {
process_cmdline: "com.android.memorylab"
}
}
}
EOF
# Run trace command referencing the file
external/perfetto/tools/record_android_trace -o java_heap.perfetto-trace \
-t 10s -c /tmp/java_heap.pbtx
Xem: Tệp báo lỗi vùng nhớ Java trên tài liệu Perfetto.
Phân tích bằng AHAT
AHAT (Công cụ phân tích heap Android) là công cụ nên dùng để xem các tệp .hprof trong trình duyệt web.
Bắt đầu AHAT
Nếu bạn đã cài đặt ahat trên đường dẫn của mình, hãy chạy nó bằng cách:
ahat heap.hprof
Hoặc chạy tệp jar độc lập:
java -jar ahat.jar heap.hprof
Sau đó, hãy mở trình duyệt để truy cập vào http://localhost:7100.
Để biết thông tin chi tiết về cách lấy hoặc tạo AHAT, hãy xem kho lưu trữ nguồn AHAT.
Quy trình phân tích chính
Tìm chỗ rò rỉ
Tìm lớp học Hoạt động (MainActivity) trong chế độ xem Phân bổ.

Nhấp vào lớp học để tìm tất cả Phiên bản.
Nhấp vào thực thể MainActivity để kiểm tra.

Trong chế độ xem thực thể, bạn có thể tìm thấy Đường dẫn mẫu từ gốc GC. Đường dẫn này cho biết chuỗi tham chiếu ngăn đối tượng được thu gom rác và Kích thước đối tượng. Kích thước này cho biết lượng bộ nhớ mà thực thể cụ thể này đang giữ lại.

Phân tích bitmap
AHAT có chế độ hỗ trợ đặc biệt để xem các đối tượng android.graphics.Bitmap. Đây thường là những đối tượng tiêu thụ nhiều bộ nhớ. Nhấp vào một thực thể Bitmap để xem bản xem trước được kết xuất của nội dung trong thực thể đó.

Trang rò rỉ hoạt động
AHAT có một chế độ xem chuyên biệt để xác định các Hoạt động bị rò rỉ, đây là một trong những lỗi rò rỉ bộ nhớ phổ biến và có tác động lớn nhất trong Android.
- Thao tác: Trong MemoryLab, hãy nhấn vào Leak an Activity (Rò rỉ một Hoạt động). Thao tác này sẽ khởi chạy
LeakedActivity, ứng dụng này cố ý tự rò rỉ. - Kết xuất: Tạo tệp báo lỗi.
- Phân tích: Nhấp vào Activity Leaks (Rò rỉ hoạt động) trong thanh bên AHAT.
- Xác minh: AHAT sẽ liệt kê
com.android.memorylab.LeakedActivitylà bị rò rỉ vì trườngmDestroyedcủa nó là true (cho biết vòng đời của activity đã kết thúc) nhưng vẫn có thể truy cập được từ một gốc GC.

So sánh tệp báo lỗi
So sánh hai kết xuất heap là một trong những cách hiệu quả nhất để xác định các vấn đề về bộ nhớ. Bằng cách so sánh một tệp kết xuất cơ sở "sạch" với một tệp kết xuất được lấy sau khi thực hiện một số thao tác, bạn có thể thấy ngay những đối tượng đã tích luỹ.
Bài tập: Xác định các điểm rò rỉ thông qua việc so sánh
Đường cơ sở: Khởi chạy MemoryLab và lấy một tệp báo lỗi vùng nhớ khối xếp cơ sở:
adb shell am dumpheap com.android.memorylab /data/local/tmp/base.hprof adb pull /data/local/tmp/base.hprof .Hành động: Nhấn vào Allocate Java Memory(10MB) (Phân bổ bộ nhớ Java (10 MB)) nhiều lần trong ứng dụng.
Cuối cùng: Lấy tệp báo lỗi thứ hai:
adb shell am dumpheap com.android.memorylab /data/local/tmp/leaked.hprof adb pull /data/local/tmp/leaked.hprof .So sánh: Khởi động AHAT với kết xuất thứ hai làm kết xuất chính và kết xuất đầu tiên làm đường cơ sở:
java -jar out/host/linux-x86/framework/ahat.jar leaked.hprof --baseline base.hprofPhân tích tổng quan: Trang Tổng quan hiện có thêm cột Δ (Delta). Bạn sẽ thấy một mức tăng lớn cho vùng nhớ
app, cho biết mức tăng đáng kể về bộ nhớ.

- Đi sâu vào dữ liệu: Nhấp vào đã root trong trình đơn. Trang này cho thấy các đối tượng có thể truy cập từ các gốc GC, được sắp xếp theo kích thước được giữ lại của chúng. Bạn sẽ thấy
MainActivityở trên cùng với mức tăng lớn.

Ghi lại dấu vết ngăn xếp phân bổ
Mặc dù Sample Path from GC Root (Đường dẫn mẫu từ gốc GC) cho bạn biết lý do một đối tượng vẫn còn tồn tại, nhưng không cho bạn biết cách đối tượng đó được tạo. Dấu vết ngăn xếp phân bổ cung cấp dòng mã chính xác đã phân bổ một đối tượng.
Khái niệm và sự đánh đổi: Việc ghi lại dấu vết ngăn xếp của mọi hoạt động phân bổ sẽ tốn nhiều tài nguyên tính toán và tiêu tốn đáng kể bộ nhớ. Trong một ứng dụng phát hành công khai lớn, điều này có thể khiến ứng dụng gần như không sử dụng được. Tuy nhiên, MemoryLab là một ứng dụng đủ nhỏ để chúng ta có thể bật tính năng theo dõi này một cách an toàn nhằm xác định chính xác nguồn gốc của các lượt phân bổ.
Bài tập: Xác định nguồn của Mảng byte
Bắt đầu bằng tính năng Theo dõi: Buộc dừng MemoryLab và khởi động lại bằng cờ
--track-allocation. Tăng độ sâu ngăn xếp mặc định để nắm bắt thêm ngữ cảnh.# Increase the allocation tracker's stack depth (requires a process restart) adb shell setprop dalvik.vm.allocTrackerMaxStack 16 adb shell am force-stop com.android.memorylab adb shell am start --track-allocation -n com.android.memorylab/.MainActivityHành động: Nhấn vào Phân bổ bộ nhớ Java(10 MB) vài lần.
Dump: Lấy và kéo một tệp báo lỗi.
Phân tích: Mở tệp kết xuất trong AHAT. Chuyển đến một phiên bản
byte[]lớn. (ví dụ: kiểm traMainActivity→mJavaAllocations(ArrayList) →elementData(Object[]) → phần tử mảng[0]).Xác minh: Trong chế độ xem thực thể, hãy xem phần Allocation Site (Vị trí phân bổ). Thao tác này sẽ cho thấy toàn bộ dấu vết ngăn xếp dẫn đến
MainActivity.allocateJava.

Tìm các chuỗi trùng lặp và tình trạng phình to do thiếu nước
Ngay cả khi một ứng dụng không có rò rỉ GC-root cổ điển, thì vùng nhớ heap Java đang hoạt động của ứng dụng đó vẫn có thể bị phình to do hàng nghìn phiên bản java.lang.String trùng lặp được tạo trong quá trình giải tuần tự hoá JSON, Protobuf, Cursor hoặc cơ sở dữ liệu Room. Các khoá lặp lại, chuỗi trạng thái, nhãn danh mục hoặc URL thường được phân bổ lại trên mọi phản hồi mạng hoặc truy vấn cơ sở dữ liệu. Trên các ứng dụng lớn về nguồn cấp dữ liệu, nhắn tin và nội dung, các chuỗi trùng lặp thường chiếm từ 30% đến 60% bộ nhớ String trực tiếp.
Cách kiểm tra các chuỗi trùng lặp trong AHAT:
- Mở trang Phân bổ rồi lọc theo
java.lang.String. - Khi so sánh 2 kết xuất heap với
--baseline, hãy kiểm tra xem số lượng phiên bảnjava.lang.Stringvà tổng số byte có tăng lên một cách không cân xứng sau khi truyền dữ liệu vào nguồn cấp dữ liệu hoặc tải bộ nhớ đệm cục bộ hay không. - Duyệt bảng thực thể
java.lang.String(được sắp xếp theo kích thước hoặc giá trị) để phát hiện các giá trị chuỗi giống hệt nhau được giữ lại trên nhiều đối tượng mô hình trong bộ nhớ.
- Giải pháp: Tránh gọi
String.intern()một cách tuỳ tiện đối với dữ liệu đầu vào tuỳ ý của người dùng hoặc mạng, vì bảng nội bộ thời gian chạy là bảng toàn cầu và có thể gây ra tình trạng tranh chấp khoá hoặc giữ lại các chuỗi lâu hơn mức cần thiết. Thay vào đó, hãy loại bỏ các chuỗi miền có tần suất cao trong quá trình khử tuần tự bằng cách sử dụng bộ nhớ đệm khử trùng lặp có phạm vi giới hạn (chẳng hạn nhưLruCache<String, String>trong trình phân tích cú pháp hoặc bộ chuyển đổi của bạn) hoặc biểu thị các tập hợp giá trị cố định dưới dạng enum hoặc hằng số nguyên.
Phân tích động lực bộ nhớ Java (hồ sơ kết hợp)
Để có thông tin đầy đủ về hành vi bộ nhớ của một ứng dụng, bạn có thể kết hợp các bộ đếm bộ nhớ, hoạt động của luồng và lập hồ sơ phân bổ dựa trên ngăn xếp lệnh gọi thành một dấu vết Perfetto duy nhất. Điều này cho phép bạn tương quan các chỉ số bộ nhớ trên toàn hệ thống (chẳng hạn như RSS và kích thước vùng nhớ heap) với quá trình thực thi mã cụ thể và các vị trí phân bổ.
Chúng ta sẽ sử dụng một cấu hình kết hợp cho phép:
- Bộ đếm bộ nhớ (
linux.process_stats): Lấy RSS và các chỉ số bộ nhớ khác. - ATrace (các danh mục
dalvik,memory,sched): Ghi lại các trạng thái luồng và sự kiện GC. - Heapprofd (
android.heapprofd): Nhắm đến cảcom.android.art(Java) vàlibc.malloc(gốc) với các kết xuất liên tục sau mỗi 5 giây.
Bài tập: phân tích bộ nhớ kết hợp
Trong bài tập này, chúng ta sẽ chạy ứng dụng MemoryLab và thực hiện một chuỗi các thao tác về bộ nhớ để quan sát các mẫu khác nhau trong dấu vết:
- Cơ sở: Trạng thái chờ.
- Java Churn: Các hoạt động phân bổ tạm thời được thu gom rác ngay lập tức.
- Persistent Java Allocation (Phân bổ Java liên tục): Phân bổ các đối tượng Java vẫn còn trong bộ nhớ.
- Phân bổ bitmap: Phân bổ các thành phần đồ hoạ lớn (nằm trong vùng nhớ khối xếp gốc/bộ nhớ đồ hoạ).
- Thu hồi: Giải phóng tất cả các tài nguyên đã phân bổ.
1. Ra mắt và chuẩn bị
Buộc dừng và khởi động lại ứng dụng để đảm bảo trạng thái hoạt động ổn định:
adb shell am force-stop com.android.memorylab adb shell am start -W -n com.android.memorylab/.MainActivity
2. Bắt đầu theo dõi và kích hoạt chuỗi
Chúng ta sẽ bắt đầu một dấu vết dài 40 giây và kích hoạt các sự kiện về bộ nhớ bằng cách sử dụng các lệnh am
broadcast.
Bắt đầu theo dõi:
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/java_memory.perfetto-trace <<EOF buffers: { size_kb: 131072 fill_policy: RING_BUFFER } data_sources: { config { name: "linux.process_stats" target_buffer: 0 process_stats_config { scan_all_processes_on_start: true proc_stats_poll_ms: 100 } } } data_sources: { config { name: "linux.ftrace" target_buffer: 0 ftrace_config { ftrace_events: "sched/sched_switch" ftrace_events: "task/task_newtask" ftrace_events: "task/task_rename" ftrace_events: "ftrace/print" atrace_categories: "dalvik" atrace_categories: "am" atrace_categories: "res" atrace_categories: "memory" atrace_categories: "sched" atrace_apps: "com.android.memorylab" } } } data_sources: { config { name: "android.heapprofd" target_buffer: 0 heapprofd_config { sampling_interval_bytes: 4096 process_cmdline: "com.android.memorylab" heaps: "libc.malloc" heaps: "com.android.art" shmem_size_bytes: 8388608 block_client: true continuous_dump_config { dump_phase_ms: 1000 dump_interval_ms: 5000 } } } } duration_ms: 40000 EOFKích hoạt trình tự (chạy các lệnh này trong thiết bị đầu cuối của máy chủ trong khi dấu vết đang chạy, tuân theo thời gian đề xuất):
# Wait ~5s for trace initialization, then start Java churn: adb shell am broadcast -a com.android.memorylab.CHURN_JAVA # Wait ~10s (at 15s mark), allocate 10MB of persistent Java memory: adb shell am broadcast -a com.android.memorylab.ALLOC_JAVA # Wait ~5s (at 20s mark), allocate 20MB of Bitmaps (native/graphics): adb shell am broadcast -a com.android.memorylab.LEAK_BITMAP # Wait ~10s (at 30s mark), free everything: adb shell am broadcast -a com.android.memorylab.FREE_ALLCách khác (Công cụ CLI): Bạn cũng có thể bắt đầu lập hồ sơ bằng cách sử dụng trực tiếp tập lệnh
heap_profile, nhắm đến cả vùng nhớ khối xếp Java và vùng nhớ khối xếp gốc bằng các kết xuất liên tục:external/perfetto/tools/heap_profile -n com.android.memorylab \ --heaps com.android.art,libc.malloc \ -c 5000 \ -d 40000 \ -o java_memory_profile
3. Phân tích dấu vết kết hợp
Mở java_memory.perfetto-trace đã thu thập trong Giao diện người dùng Perfetto.
Các dấu vết chính trong Perfetto
Trước khi phân tích dòng thời gian, hãy xác định vị trí của những dấu vết cần thiết này cho quy trình com.android.memorylab:
mem.rss.anon(RSS ẩn danh): Nằm trong phần Bộ nhớ của quy trình. Theo dõi này đo lường bộ nhớ vật lý (RAM) được hệ điều hành phân bổ cho quy trình. Đây là mức sử dụng bộ nhớ thực tế.Heap size (KB): Cũng nằm trong mục Bộ nhớ. Đây là một bộ đếm dành riêng cho Dalvik/ART, đại diện cho không gian địa chỉ ảo được dành riêng cho vùng nhớ khối xếp Java. Nó phản ánh giới hạn heap nội bộ của VM, dao động khi các đối tượng được phân bổ và GC chạy.HeapTaskDaemon: Nằm trong danh sách các luồng trong quy trình. Đây là luồng ở chế độ nền mà Trình thu gom rác ART thực hiện hầu hết các thao tác. Hoạt động ở đây cho biết các lượt truy cập GC đang hoạt động.- Kết xuất phân bổ liên tục (heapprofd): Xuất hiện dưới dạng các lát có màu dọc theo dòng thời gian trên cùng. Mỗi lát cắt đại diện cho một khoảng thời gian. Khi nhấp vào một lát cắt hoặc chọn một dải thời gian, bạn có thể kiểm tra Flamegraph (trong ngăn dưới cùng) cho com.android.art (phân bổ Java) hoặc libc.malloc (phân bổ gốc) để xem những gì đã được phân bổ trong khoảng thời gian đó.
Phân tích theo giai đoạn thời gian
Hãy xem xét dấu vết theo trình tự thời gian để xem các dấu vết này tương tác như thế nào trong từng giai đoạn của bài tập.
Giai đoạn 1: đường cơ sở (0 giây – 5 giây)
- Tình trạng hiện tại: Ứng dụng đang ở trạng thái rảnh, chờ lệnh.
- Trạng thái theo dõi:
mem.rss.anon: Đường thẳng ngang ở đường cơ sở (thường là khoảng 60 – 80 MB, tuỳ thuộc vào thiết bị).Heap size (KB): Đường thẳng, khớp với việc phân bổ vùng nhớ heap ban đầu của Java.HeapTaskDaemon: Trạng thái rảnh (không có lát cắt nào cho thấy quá trình thực thi).- Allocation Dumps (Kết xuất phân bổ): Hiển thị mức phân bổ cơ sở tối thiểu.

Giai đoạn 2: Phân bổ Java (5 giây – 15 giây)
- Điều gì đang xảy ra:
AllocationChurnThreadđược khởi động, liên tục phân bổ các mảng 1 MB và loại bỏ chúng. - Trạng thái theo dõi:
Heap size (KB): Cho thấy một mẫu răng cưa nhanh. Kích thước vùng nhớ tạm tăng lên khi các hoạt động phân bổ tích luỹ và giảm mạnh khi GC chạy.HeapTaskDaemon: Cho thấy hoạt động gần như không đổi, với các lát thực thi hoàn toàn phù hợp với các điểm giảm trong răng cưaHeap size.mem.rss.anon: Theo dõi hoạt động của vùng nhớ Java.- Tệp kết xuất phân bổ vùng nhớ heap Java: Khi chọn các lát trong dấu vết này, bạn sẽ thấy các hoạt động phân bổ vùng nhớ heap com.android.art.
Các mẫu phân bổ cho thấy AllocationChurnThread là trình phân bổ chính, với tất cả các hoạt động phân bổ đều dùng chung cùng một ngăn xếp lệnh gọi trỏ đến lambda bên trong MainActivity.java.

Giai đoạn 3: phân bổ Java liên tục (15 giây – 20 giây)
- Điều gì đang xảy ra: Chúng tôi phân bổ 10 MB đối tượng Java và giữ một tham chiếu đến các đối tượng đó trong
mJavaAllocations. - Trạng thái theo dõi:
Heap size (KB): Đường cơ sở của các bước răng cưa tăng lên khoảng 10 MB.mem.rss.anon: Tăng khoảng 10 MB, vì hệ điều hành phải hỗ trợ việc phân bổ liên tục này bằng các trang thực mới.- Tệp kết xuất phân bổ vùng nhớ heap Java: Khi chọn các lát trong dấu vết này, bạn sẽ thấy các hoạt động phân bổ vùng nhớ heap com.android.art.
- Kết xuất phân bổ (Flamegraph): Việc kiểm tra vùng nhớ heap com.android.art cho kết xuất được thực hiện trong cửa sổ này cho thấy một đường dẫn phân bổ mới từ
MainActivity.allocateJavagóp phần vào kích thước được giữ lại.
Chọn một mẫu phân bổ bao gồm khoảng thời gian trùng với mức tăng 10 MB cho việc phân bổ liên tục. Bạn sẽ thấy các callstack phân bổ phân kỳ thành hai vị trí khác nhau, một chịu trách nhiệm cho cùng một mức phân bổ ngắn hạn mà chúng ta đã thấy trước đây và vị trí còn lại cho mức phân bổ dài hạn mới.

Giai đoạn 4: phân bổ bitmap (20 giây – 30 giây)
- Điều gì đang xảy ra: Chúng ta cũng phân bổ 20 MB cho các Bitmap.
- Trạng thái theo dõi:
Heap size (KB): Tương tự như trước.mem.rss.anon: Cho thấy mức tăng đáng kể khoảng 20 MB, tương ứng với các hoạt động phân bổ gốc cho dữ liệu pixel Bitmap.- Allocation Dumps (Flamegraph): Lần này, hãy tập trung vào các lát cho vùng nhớ heap libc.malloc (Native).
Các ngăn xếp lệnh phân bổ gốc cho thấy quá trình phân bổ Bitmap bắt nguồn từ các thư viện đồ hoạ gốc. Đây là một trường hợp sử dụng phù hợp cho tính năng theo dõi hoạt động phân bổ gốc, vì bạn sẽ không thấy các hoạt động phân bổ Bitmap này trong vùng nhớ heap của Java.

Giai đoạn 5: phục hồi (30 giây – 40 giây)
- Điều gì đang xảy ra: Chúng tôi kích hoạt
FREE_ALL, xoá các tham chiếu đến tất cả các bitmap và hoạt động phân bổ Java liên tục, sau đó là mộtSystem.gc()rõ ràng. - Trạng thái theo dõi:
Heap size (KB): Giảm xuống mức cơ sở.mem.rss.anon: Giảm xuống, cho thấy hệ điều hành đang thu hồi các trang thực.HeapTaskDaemon: Cho thấy một loạt hoạt động cuối cùng khi xử lý quá trình thu gom rác.

Giám sát các lỗi hết bộ nhớ trước đây (ApplicationExitInfo)
Việc nắm bắt LMK khi sự cố xảy ra rất hữu ích cho việc gỡ lỗi chủ động, nhưng đối với phép đo từ xa tại hiện trường, bạn có thể sử dụng API ApplicationExitInfo. Nhờ đó, ứng dụng của bạn có thể phát hiện lý do khiến ứng dụng bị chấm dứt trong một phiên trước đó.
ActivityManager am = getSystemService(ActivityManager.class);
List<ApplicationExitInfo> exitReasons = am.getHistoricalProcessExitReasons(null, 0, 1);
if (!exitReasons.isEmpty()) {
ApplicationExitInfo info = exitReasons.get(0);
if (info.getReason() == ApplicationExitInfo.REASON_LOW_MEMORY) {
// App was killed by the system Low Memory Killer
}
}
Các phương pháp hay nhất
- Cơ sở đầu tiên: Luôn lấy một tệp báo lỗi "cơ sở" sau khi ứng dụng đã khởi tạo nhưng trước khi thực hiện hành động mà bạn đang kiểm thử.
- Sử dụng trang Rò rỉ hoạt động của AHAT: AHAT có một trang Rò rỉ hoạt động chuyên dụng, tự động xác định các thực thể Hoạt động đã bị huỷ nhưng vẫn được lưu giữ trong bộ nhớ. Đây thường là cách nhanh nhất để tìm ra các rò rỉ phổ biến.
- Kiểm tra đường dẫn đến các gốc GC: Đối với mọi đối tượng bị rò rỉ, hãy sử dụng chế độ xem Đường dẫn từ gốc trong AHAT để hiểu chính xác tham chiếu nào đang giữ cho đối tượng đó hoạt động (ví dụ: một trường tĩnh, một luồng chạy trong thời gian dài hoặc một trình nghe đã đăng ký).
← Tools (Công cụ) | ↑ Up (Lên) | Bitmaps → (Bitmap)