Heap Dump: nó là gì, phân tích heap và loại bỏ rò rỉ bộ nhớ

Tác giả: IT Sectr Đã đăng: 2026-05-07 Thời gian đọc: 10 phút

Heap Dump (kết xuất heap) là một ảnh chụp bộ nhớ động của ứng dụng chứa thông tin đầy đủ về tất cả các đối tượng còn sống: lớp, kích thước, tham chiếu lẫn nhau và khả năng truy cập từ các gốc GC. Heap Dump là công cụ chính để phân tích rò rỉ bộ nhớ và tối ưu hóa mức tiêu thụ tài nguyên. Theo Android Developers, phân tích heap dump có thể phát hiện tới 95% rò rỉ bộ nhớ, bao gồm tham chiếu vòng, listener bị quên và tham chiếu tĩnh chưa được giải phóng.

Các Điểm Chính

  • Heap Dump là ảnh chụp toàn bộ bộ nhớ động của ứng dụng với thông tin về từng đối tượng và tham chiếu giữa chúng.
  • Android Studio Memory Profiler cho phép chụp heap dump theo thời gian thực cho ứng dụng Java và Kotlin.
  • Xcode Instruments cung cấp công cụ Allocations để tạo và phân tích heap dump trên iOS/macOS.
  • Shallow và retained size là các chỉ số chính: shallow là kích thước của bản thân đối tượng, retained là kích thước của đối tượng cộng với tất cả đối tượng mà nó giữ lại.
  • Phân tích heap dump bao gồm tìm kiếm trong dominator tree, các đối tượng retained lớn nhất và đường dẫn ngắn nhất đến gốc GC.

Heap dump là gì và tại sao cần nó

Heap dump là kết xuất đầy đủ heap của máy ảo — vùng bộ nhớ nơi tất cả các đối tượng được tạo động lưu trú. Trong Java và Kotlin, đó là heap Dalvik/ART trên Android, trong Swift và Objective-C, đó là heap được quản lý bởi ARC trên iOS. Heap dump chụp lại mọi đối tượng, lớp của nó, kích thước, trường, tham chiếu đến các đối tượng khác và cờ khả năng truy cập từ gốc GC (biến ngăn xếp, trường tĩnh, tham chiếu JNI).

Mục đích chính của heap dump là phát hiện rò rỉ bộ nhớ. Rò rỉ xảy ra khi một ứng dụng tiếp tục giữ tham chiếu đến các đối tượng không còn cần thiết, ngăn chặn việc thu gom của bộ thu gom rác. Nguyên nhân điển hình: listener sự kiện không được hủy đăng ký khi activity bị hủy; singleton có tham chiếu đến context; closure capture self; bộ sưu tập tĩnh nơi dữ liệu được thêm vào mà không xóa. Heap dump cung cấp một bức tranh chính xác: đối tượng nào “còn sống”, đối tượng nào không cần thiết và ai đang tham chiếu chúng.

Theo Google I/O, hơn 60% báo cáo sự cố của ứng dụng Android liên quan đến OutOfMemoryError và trong 80% trường hợp, nguyên nhân gốc rễ là rò rỉ bộ nhớ có thể phát hiện qua heap dump. Đối với ứng dụng iOS, tình hình tương tự: rò rỉ do retain cycles là một trong những nguyên nhân phổ biến nhất gây sự cố, được xác định thông qua công cụ Allocations trong Xcode.

Khi nào cần heap dump

Heap dump nên được thực hiện khi xuất hiện các triệu chứng sau: ứng dụng tiêu thụ bộ nhớ tuyến tính trong các thao tác lặp đi lặp lại (điều hướng qua lại giữa các màn hình); sau khi đóng màn hình, bộ nhớ không trở về mức cơ bản; xuất hiện OutOfMemoryError hoặc cảnh báo bộ nhớ trên iOS; ứng dụng kết thúc do vượt quá giới hạn bộ nhớ. Việc thu thập heap dump thường xuyên là một phần của quy trình văn hóa kỹ thuật trong các dự án di động lớn như Instagram và Spotify.

Heap dump trong Android Studio: thu thập và phân tích

Android Studio cung cấp Memory Profiler — một công cụ tích hợp để chụp heap dump theo thời gian thực. Có thể truy cập qua View → Tool Windows → Profiler. Sau khi khởi chạy ứng dụng, chọn phiên, chuyển đến tab Memory và nhấp vào Dump Java Heap. Android Studio tạm dừng ứng dụng, thực hiện kết xuất heap ART và tải kết quả để phân tích. Tệp kết xuất có định dạng .hprof — tiêu chuẩn HPROF tương thích với hầu hết các trình phân tích bộ nhớ.

Sau khi tải kết xuất, Android Studio hiển thị bảng đối tượng với các cột: Allocations (số lượng phiên bản), Native Size (bộ nhớ ngoài heap ART), Shallow Size (bộ nhớ của bản thân đối tượng), Retained Size (bộ nhớ của đối tượng bao gồm toàn bộ đồ thị con). Lọc theo tên lớp, sắp xếp theo retained size và tìm kiếm theo gói cho phép bạn nhanh chóng tìm thấy các khu vực có vấn đề.

kotlin
// Rò rỉ điển hình — listener không được hủy đăng ký trong onDestroy
class MainActivity : AppCompatActivity() {
    private val sensorManager by lazy {
        getSystemService(SENSOR_SERVICE) as SensorManager
    }
    private val listener = MySensorListener()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sensorManager.registerListener(listener,
            sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT),
            SensorManager.SENSOR_DELAY_NORMAL)
    }

    override fun onDestroy() {
        super.onDestroy()
        // ❌ Thiếu sensorManager.unregisterListener(listener)
        // → Activity sẽ không được GC, heap dump sẽ hiển thị rò rỉ
    }
}

Phân tích dominator tree trong Android Studio

Tab Dominator Tree hiển thị các đối tượng giữ nhiều bộ nhớ nhất. Nếu một đối tượng bị xóa khỏi dominator tree, tất cả bộ nhớ mà nó giữ sẽ có sẵn cho việc thu gom. Đây là công cụ chính: thay vì quét hàng nghìn đối tượng, bạn tập trung vào 10–20 đối tượng kiểm soát 80–90% bộ nhớ. Theo Google, phân tích dominator tree là cách hiệu quả nhất để tìm điểm rò rỉ, giảm thời gian phân tích từ giờ xuống phút.

Heap dump trong Xcode Instruments: Allocations và Leaks

Xcode Instruments cung cấp hai công cụ để làm việc với heap dump: Allocations — chụp kết xuất heap với biểu đồ tiêu thụ thời gian thực; Leaks — tự động tìm kiếm rò rỉ thông qua phân tích retain cycles. Allocations hiển thị tất cả các đối tượng trong heap, kích thước của chúng, số lần tạo (allocations) và giải phóng (deallocations). Sự khác biệt giữa số lần tạo và giải phóng cho một lớp cụ thể cho thấy rò rỉ tiềm ẩn.

Chụp heap dump trong Allocations được thực hiện bằng nút Snapshot Memory — công cụ tạm dừng ứng dụng và chụp kết xuất đầy đủ. Sau đó, các chế độ xem tiêu chuẩn có sẵn: danh sách đối tượng theo lớp, cây gọi (call tree) cho mỗi đối tượng và trình tạo báo cáo. Không giống như Android Studio, Xcode không sử dụng .hprof mà lưu trữ dữ liệu ở định dạng .trace riêng tương thích với Instruments.

swift
// Rò rỉ iOS điển hình — retain cycle qua closure
class NetworkManager {
    var onComplete: ((Data) -> Void)?

    func startRequest() {
        // ❌ Closure capture self — retain cycle
        onComplete = { data in
            self.process(data)
        }
    }
    func process(_ data: Data) {}
}

Công cụ Leaks tự động phát hiện retain cycles và rò rỉ thông qua phân tích đồ thị tham chiếu. Nó đánh dấu các đối tượng bị rò rỉ bằng biểu tượng màu tím và hiển thị đường dẫn đến gốc (GC root). Để loại bỏ retain cycle, chỉ cần thêm [weak self] hoặc [unowned self] trong capture của closure. Việc chạy thường xuyên công cụ Leaks là một bước bắt buộc trong quy trình CI ở các nhóm sử dụng Swift để phát triển iOS.

swift
// Sửa lỗi — tham chiếu yếu đến self
onComplete = { [weak self] data in
    guard let self else { return }
    self.process(data)
}

Shallow size, retained size và dominator tree

Để phân tích chính xác heap dump, cần hiểu ba chỉ số chính. Shallow size là lượng bộ nhớ mà đối tượng chiếm trực tiếp: các trường, tiêu đề và căn chỉnh. Đối với một đối tượng Java/Kotlin điển hình, shallow size là 16–40 byte. Retained size là shallow size của đối tượng cộng với tổng shallow size của tất cả các đối tượng chỉ có thể truy cập qua đối tượng này. Retained size cho thấy tác động thực tế của đối tượng lên mức tiêu thụ bộ nhớ.

Chỉ sốMô tảVí dụ
Shallow sizeKích thước của bản thân đối tượng (byte)Bitmap (100×100) = 40.016 B
Retained sizeShallow size + mọi thứ nó giữActivity với View Tree = 2–5 MB
Deep sizeRetained size + đối tượng lồng từ đồ thị khácScrollView với bộ điều hợp = 10–50 MB

Dominator tree là cấu trúc nơi mỗi đối tượng tham chiếu đến “kẻ thống trị” của nó — đối tượng kiểm soát khả năng truy cập của nó. Nếu kẻ thống trị bị xóa, tất cả các đối tượng trong cây con của nó trở thành rác. Phân tích dominator tree là cách nhanh nhất để tìm ra đối tượng nào giữ nhiều bộ nhớ nhất. Theo Eclipse MAT, 90% rò rỉ được phát hiện bằng cách xem xét top-20 dominator tree trong 5 phút.

Phân tích rò rỉ bộ nhớ qua heap dump

Quy trình phân tích rò rỉ qua heap dump bao gồm nhiều bước. Bước 1: thực hiện hành động sẽ giải phóng bộ nhớ (đóng màn hình, kết thúc thao tác). Bước 2: gọi GC và chụp heap dump. Bước 3: tìm các đối tượng lẽ ra đã bị hủy. Bước 4: đối với đối tượng nghi ngờ, chạy Path to GC Roots — chuỗi tham chiếu giữ cho đối tượng tồn tại. Tham chiếu cuối cùng trong chuỗi là nguyên nhân rò rỉ.

Path to GC Roots

Chức năng Path to GC Roots có sẵn trong Android Studio Profiler, Eclipse MAT và Xcode Instruments. Nó hiển thị chuỗi tham chiếu ngắn nhất từ gốc GC đến đối tượng có vấn đề. Bằng cách loại trừ các tham chiếu yếu (weak) và mềm (soft), bạn chỉ nhận được các tham chiếu mạnh (strong) — những tham chiếu thực sự ngăn chặn việc thu gom. Theo Square Engineering, 70% rò rỉ trong ứng dụng Android chỉ do hai mẫu gây ra: tham chiếu tĩnh đến Activity hoặc Context và listener đã đăng ký nhưng không hủy đăng ký.

kotlin
// Ví dụ rò rỉ qua tham chiếu tĩnh
object AppCache {
    private val cache = mutableMapOf<String, Any>()

    fun storeActivityReference(activity: Activity) {
        cache["current_activity"] = activity // ❌ Rò rỉ!
    }
}

// Sửa lỗi: tham chiếu yếu
object AppCacheFixed {
    private val cache = mutableMapOf<String, WeakReference<Any>>()
}

So sánh hai heap dump

Kỹ thuật chế độ so sánh là một trong những phương pháp hiệu quả nhất để tìm rò rỉ. Chụp heap dump trước và sau một hành động lặp đi lặp lại. So sánh số lượng phiên bản của các lớp chính: nếu số lượng Activity tăng lên mặc dù tất cả các activity đã đóng — đó là rò rỉ. Android Studio và Eclipse MAT hỗ trợ so sánh kết xuất tự động với làm nổi bật sự khác biệt. Theo Google, so sánh kết xuất cho phép tìm thấy rò rỉ không nhìn thấy trong phân tích đơn lẻ nhờ hiệu ứng tích lũy.

Khuyến nghị thực tế để giảm tiêu thụ bộ nhớ

Dựa trên phân tích heap dump trong các dự án thực tế, các phương pháp tối ưu hóa bộ nhớ đã được chứng minh đã được phát triển. Sử dụng WeakReference cho bộ nhớ đệm, callback và tham chiếu context trong các đối tượng tồn tại lâu dài. Hủy đăng ký listener trong onPause/onDestroy cho Android và deinit cho iOS. Tránh các bộ sưu tập tĩnh lớn — nếu cần, hãy sử dụng LruCache với giới hạn kích thước. Tối ưu hóa Bitmap: tải hình ảnh với inSampleSize chính xác, sử dụng Glide hoặc Picasso với bộ nhớ đệm đĩa.

Hồ sơ bộ nhớ trong quá trình phát triển

Bao gồm việc chụp heap dump thường xuyên trong quy trình CI của bạn. Thiết lập một tác vụ chạy kiểm thử UI có công cụ, thực hiện các kịch bản người dùng chính và so sánh heap dump với đường cơ sở. Nếu retained size tăng hơn 5% so với đường cơ sở, bản dựng được đánh dấu là hồi quy. Cách tiếp cận này được thực hành tại Airbnb, Uber và các công ty khác có yêu cầu chất lượng cao. Theo Uber Engineering, việc triển khai phân tích heap dump tự động trong CI đã giảm 70% lỗi liên quan đến bộ nhớ trong một quý.

groovy
// Ví dụ tác vụ Gradle cho heap dump tự động trong CI
task profileMemory(type: Exec) {
    commandLine 'adb', 'shell',
        'am start -n com.example/.MainActivity'
    // Đang chờ tải
    doLast {
        exec { commandLine 'adb', 'shell',
            'am broadcast -a com.example.DUMP_HEAP' }
    }
}

Câu Hỏi Thường Gặp

Sự khác biệt giữa shallow size và retained size là gì?

Shallow size là kích thước của bản thân đối tượng (trường + tiêu đề). Retained size là kích thước của đối tượng cộng với tất cả các đối tượng sẽ trở thành rác nếu nó bị xóa. Retained size là chỉ số chính cho thấy tác động của đối tượng lên mức tiêu thụ bộ nhớ.

Làm thế nào để chụp heap dump trên thiết bị Android vật lý?

Qua Android Studio Profiler, chọn thiết bị và tiến trình, nhấp vào Dump Java Heap. Thay thế — qua dòng lệnh: adb shell am dumpheap PID /sdcard/dump.hprof, sau đó adb pull.

Tại sao heap dump có thể rất lớn (500 MB+)?

Heap dump bao gồm tất cả các đối tượng còn sống. Nếu ứng dụng sử dụng bộ nhớ đệm, Bitmap hoặc xử lý dữ liệu lớn, kết xuất có thể đạt tới hàng trăm megabyte. Lọc theo lớp hoặc sử dụng Eclipse MAT để chỉ tải chỉ mục.

Có thể phân tích heap dump mà không cần Android Studio không?

Có, hãy sử dụng Eclipse MAT (Memory Analyzer Tool) — một công cụ miễn phí để phân tích tệp .hprof. Nó hỗ trợ dominator tree, path to GC roots, so sánh kết xuất và phát hiện rò rỉ tự động qua Leak Suspects Report.

Heap dump có làm giảm hiệu suất ứng dụng không?

Bản thân kết xuất — có, vì việc thu thập kết xuất tạm dừng tất cả các luồng (stop-the-world). Không có kết xuất — không. Thực hiện kết xuất trong môi trường được kiểm soát (băng thử nghiệm, CI), không phải trên môi trường sản xuất.

Tóm Tắt

  • Heap Dump là ảnh chụp đầy đủ heap của ứng dụng với thông tin về từng đối tượng và mối quan hệ giữa chúng.
  • Android Studio Memory Profiler và Xcode Instruments Allocations là các công cụ chụp kết xuất chính.
  • Shallow size là kích thước của bản thân đối tượng; retained size là kích thước của đối tượng với toàn bộ đồ thị con phụ thuộc.
  • Dominator tree hiển thị các đối tượng kiểm soát nhiều bộ nhớ nhất.
  • Path to GC Roots là chuỗi tham chiếu mạnh ngăn đối tượng bị thu gom rác.
  • So sánh hai heap dump (trước/sau hành động) là phương pháp đáng tin cậy nhất để phát hiện rò rỉ.
  • Tự động hóa việc chụp và phân tích heap dump trong CI ngăn ngừa hồi quy bộ nhớ trong quá trình phát triển.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm