LeakCanary — nó là gì, thư viện tìm rò rỉ trong Android

Tác giả: IT Sectr Đã đăng: 2026-03-30 Thời gian đọc: 9 phút

LeakCanary là thư viện mã nguồn mở của Square để tự động phát hiện rò rỉ bộ nhớ trong các ứng dụng Android. Nó tích hợp vào quy trình phát triển và theo dõi thời gian thực vòng đời của Activity, Fragment, ViewModel và các thành phần khác, báo hiệu rò rỉ ngay khi chúng xảy ra. Theo Square Open Source, thư viện được sử dụng trong hàng nghìn dự án và được coi là tiêu chuẩn thực tế cho chẩn đoán bộ nhớ trên Android.

Những điểm chính

  • LeakCanary là thư viện phát hiện tự động rò rỉ bộ nhớ trên Android.
  • Cơ chế hoạt động dựa trên WeakReference và kích hoạt GC thủ công sau khi thành phần bị hủy.
  • Heap dump được tạo tự động khi phát hiện rò rỉ và được phân tích bởi bộ phân tích tích hợp.
  • Kết quả — một chuỗi tham chiếu chính xác (leak trace) chỉ ra vị trí rò rỉ trong mã.
  • LeakCanary 2.x không yêu cầu cấu hình thủ công — chỉ cần một dependency trong build.gradle.

LeakCanary là gì?

LeakCanary là thư viện phát hiện tự động rò rỉ bộ nhớ trong ứng dụng Android, được phát triển bởi Square. Nó tích hợp vào quy trình xây dựng ứng dụng và tự động giám sát xem các đối tượng cần bị hủy (Activity, Fragment, View) có còn trong bộ nhớ hay không. Khi phát hiện rò rỉ, LeakCanary tạo heap dump và phân tích chuỗi tham chiếu giữ đối tượng.

Thư viện đã trở thành tiêu chuẩn trong cộng đồng Android: theo GitHub, dự án có hơn 28 nghìn sao và được sử dụng trong các ứng dụng của Google, Uber, Airbnb và Facebook. LeakCanary có sẵn hai phiên bản chính: cổ điển 1.x (với cấu hình thủ công) và hiện đại 2.x (tích hợp tự động qua ContentProvider). Phiên bản 2.x không yêu cầu sửa đổi lớp Application — chỉ cần dependency là đủ để hoạt động đầy đủ.

Nhiệm vụ chính của LeakCanary là phát hiện khi một đối tượng tiếp tục tồn tại trong bộ nhớ sau khi vòng đời của nó kết thúc. Điều này điển hình cho rò rỉ qua các trường tĩnh, singleton, callback chưa hủy đăng ký, lớp ẩn danh và closure capture đối tượng bên ngoài.

Tại sao LeakCanary quan trọng cho phát triển Android

Rò rỉ bộ nhớ trên Android nghiêm trọng hơn trên máy tính để bàn do RAM hạn chế trên thiết bị di động. Ngay cả rò rỉ 5–10 MB mỗi lần chuyển màn hình cũng có thể dẫn đến OutOfMemoryError sau 30–40 phút sử dụng ứng dụng. LeakCanary phát hiện những vấn đề này ngay từ giai đoạn phát triển, không cần chờ sự cố trên production.

LeakCanary hoạt động như thế nào?

LeakCanary sử dụng tham chiếu yếu (WeakReference) kết hợp với thu gom rác cưỡng chế. Khi Activity hoặc Fragment gọi onDestroy, LeakCanary tạo WeakReference đến đối tượng đó và kích hoạt GC sau một độ trễ ngắn (mặc định 5 giây). Nếu đối tượng vẫn có thể truy cập qua WeakReference sau GC, nó đang bị giữ bởi một tham chiếu mạnh — một rò rỉ được ghi nhận.

Sau khi phát hiện rò rỉ, LeakCanary tạo heap dump (kết xuất bộ nhớ) — ảnh chụp toàn bộ bộ nhớ ứng dụng ở định dạng HPROF. Sau đó, bộ phân tích tích hợp (Shark cho phiên bản 2.x) xây dựng đồ thị khả năng truy cập từ GC Roots đến đối tượng bị rò rỉ và tìm đường đi ngắn nhất — chuỗi tham chiếu giữ đối tượng trong bộ nhớ.

kotlin
// Logic phát hiện LeakCanary đơn giản hóa
class ObjectWatcher {
    private val watchedReferences = CopyOnWriteArrayList<KeyedWeakReference>()

    fun watch(watchedObject: Any, description: String) {
        val reference = KeyedWeakReference(watchedObject, description)
        watchedReferences.add(reference)
        BackgroundHandler.postDelayed({
            checkForLeaks()
        }, 5000)
    }

    private fun checkForLeaks() {
        GcTrigger.runGc() // GC cưỡng chế
        for (ref in watchedReferences) {
            if (ref.get() != null) {
                onLeakFound(ref) // đối tượng sống sót qua GC — đó là rò rỉ
            }
        }
    }
}

Điểm mấu chốt là lệnh gọi cưỡng chế đến GcTrigger.runGc(). Nếu không có nó, không thể phân biệt đối tượng thực sự bị rò rỉ với đối tượng mà GC chưa kịp thu gom. LeakCanary thực hiện việc này tới ba lần: nếu sau ba chu kỳ GC đối tượng vẫn còn trong bộ nhớ, rò rỉ được xác nhận.

Shark là gì — bộ phân tích heap dump

Shark là bộ phân tích heap dump tích hợp trong LeakCanary 2.x, được viết bằng Kotlin. Không giống như bộ phân tích HAHA trước đây, Shark không tải toàn bộ tệp HPROF vào bộ nhớ mà duyệt đồ thị đối tượng với mức cấp phát tối thiểu. Điều này giảm tiêu thụ RAM trong quá trình phân tích từ 50 MB xuống 2–5 MB và rút ngắn thời gian phân tích từ 30 giây xuống 1–3 giây.

Cách cài đặt và cấu hình LeakCanary

Cài đặt LeakCanary 2.x trong một dự án Android hiện đại chỉ mất một dòng trong build.gradle. Thư viện sử dụng ContentProvider để khởi tạo tự động — không cần sửa đổi lớp Application hay thêm mã vào MainActivity. Dependency chỉ được thêm cho bản build debug để APK phát hành không chứa mã thừa.

groovy
// build.gradle (app/module)
dependencies {
    // debugImplementation — thư viện chỉ cho bản build debug
    debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}

Sau khi thêm dependency và xây dựng lại dự án, LeakCanary tự động xuất hiện trong ứng dụng. Ở lần khởi chạy đầu tiên, thư viện hiển thị thông báo hệ thống xác nhận kích hoạt. Tất cả rò rỉ được phát hiện hiển thị dưới dạng thông báo — chạm vào thông báo sẽ mở màn hình với báo cáo chi tiết (LeakTrace).

Để tùy chỉnh, bạn có thể tạo AppWatcherInstaller riêng và ghi đè các tham số: thời gian chờ GC, danh sách loại đối tượng theo dõi, bật lưu heap dump vào đĩa. Tuy nhiên, đối với 90% dự án, cấu hình mặc định là tối ưu.

Cấu hình cho coroutine và Jetpack Compose

Bắt đầu từ phiên bản 2.12, LeakCanary hỗ trợ theo dõi tự động ViewModel, phạm vi coroutine và đối tượng State của Compose. Không cần dependency bổ sung — thư viện tự động phát hiện thành phần Jetpack nào đang được sử dụng trong dự án và kích hoạt các bộ dò tương ứng.

Cách đọc báo cáo LeakCanary

Báo cáo LeakCanary (LeakTrace) là một chuỗi tham chiếu nhiều dòng từ GC Root đến đối tượng bị rò rỉ. Mỗi dòng hiển thị lớp và trường mà qua đó một tham chiếu mạnh đi qua. Nhà phát triển cần đọc chuỗi từ dưới lên trên: dòng dưới cùng là đối tượng bị rò rỉ, dòng trên cùng là điểm vào (GC Root).

Một LeakTrace điển hình trông như sau: GC Root → trường tĩnh của Application → singleton → callback → Activity. Nếu nhà phát triển thấy chuỗi như vậy, vấn đề đã rõ: singleton giữ một callback đã capture tham chiếu đến Activity. Giải pháp là thay tham chiếu mạnh bằng tham chiếu yếu trong singleton.

text
┬
├─ android.app.Application
│    Leaking: NO (Application — singleton)
│    ↓ Application.leakedActivities
├─ java.util.ArrayList
│    Leaking: NO (ArrayList — normal)
│    ↓ ArrayList[0]
├─ com.example.MainActivity
│    Leaking: YES (Activity destroyed but still in memory)
│    ↓ MainActivity.mCallback
├─ com.example.CallbackWrapper
│    Leaking: UNKNOWN
│    ↓ CallbackWrapper.mListener
│              ~~~~~~~~~~
├─ com.example.MyCallback (anonymous)
│    Leaking: UNKNOWN
│    ↓ MyCallback.this$0
├─ com.example.MainActivity
│    Leaking: YES (MainActivity is the leak)
╰

Trong ví dụ này, LeakCanary cho thấy MainActivity bị giữ qua chuỗi: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → MainActivity một lần nữa. Mũi tên this$0 chỉ ra rằng lớp ẩn danh MyCallback đã capture tham chiếu bên ngoài đến Activity. Giải pháp là biến callback thành tham chiếu yếu hoặc hủy nó trong onDestroy.

LeakCanary cũng hiển thị trạng thái rò rỉ cho mỗi phần tử trong chuỗi: NO (không rò rỉ — phần tử gốc), YES (đối tượng nên bị hủy), UNKNOWN (không thể xác định trạng thái). Trạng thái UNKNOWN không có nghĩa là vấn đề — đó là đối tượng trung gian mà LeakCanary không thể phân loại dứt khoát.

LeakCanary 2.x so với 1.x: khác biệt chính

Sự chuyển đổi từ phiên bản 1.x lên 2.x là căn bản: các nhà phát triển đã viết lại thư viện từ đầu, thay thế bộ phân tích HAHA cũ bằng công cụ riêng Shark, được viết bằng Kotlin. Shark nhanh hơn nhiều lần, yêu cầu ít bộ nhớ hơn cho phân tích và xác định nguyên nhân gốc rễ của rò rỉ chính xác hơn.

Tham sốLeakCanary 1.xLeakCanary 2.x
Ngôn ngữ phân tíchJava (HAHA — fork của Android SDK)Kotlin (Shark — công cụ riêng)
Cài đặtCấu hình thủ công AppWatcher trong ApplicationTự động qua ContentProvider
Tốc độ10–30 giây để phân tích heap dump1–5 giây để phân tích heap dump
Hiệu suấtTiêu thụ 10–50 MB RAM khi phân tíchTiêu thụ 2–10 MB RAM khi phân tích

Lợi thế chính của Shark là nó không tải toàn bộ heap dump vào bộ nhớ, mà duyệt đồ thị tham chiếu với mức cấp phát tối thiểu. Điều này làm cho LeakCanary 2.x phù hợp để sử dụng trên thiết bị có RAM thấp mà không có nguy cơ OutOfMemoryError trong quá trình phân tích.

Phiên bản 2.x cũng giới thiệu khả năng xuất heap dump ra tệp để phân tích sau trong Android Studio Memory Profiler. Để thực hiện, hãy bật cài đặt dumpHeapWhenLeakFound trong cấu hình AppWatcher.

Các rò rỉ điển hình được LeakCanary tìm thấy

LeakCanary phát hiện hiệu quả nhiều loại rò rỉ phổ biến trên Android. Phổ biến nhất là rò rỉ qua tham chiếu tĩnh đến Activity — nhà phát triển giữ tham chiếu đến ngữ cảnh Activity trong singleton, và Activity không thể được GC thu gom sau khi vòng đời kết thúc.

Loại phổ biến thứ hai là rò rỉ qua trình lắng nghe chưa hủy đăng ký. Nếu registerListener được gọi trong onStart nhưng unregisterListener không được gọi trong onStop/onDestroy, đối tượng lắng nghe bị hệ thống giữ lại ngay cả sau khi activity bị hủy. LeakCanary hiển thị rõ ràng trình lắng nghe nào và trong dịch vụ hệ thống nào còn sống.

kotlin
// Rò rỉ điển hình: Activity bị capture trong callback của singleton
object AnalyticsManager {
    private var callback: ((String) -> Unit)? = null

    fun register(callback: (String) -> Unit) {
        this.callback = callback // tham chiếu mạnh đến callback
    }

    fun unregister() {
        callback = null // ĐỪNG QUÊN gọi trong onDestroy!
    }
}

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        AnalyticsManager.register { event ->
            logEvent(event) // lambda capture this
        }
        // nếu không gọi unregister trong onDestroy → rò rỉ Activity
    }
}

Loại thứ ba là rò rỉ qua Fragment trong BackStack. Nếu FragmentTransaction.addToBackStack() được gọi mà không xóa Fragment khi quay lại, các phiên bản Fragment cũ vẫn còn trong bộ nhớ. LeakCanary giúp phát hiện những rò rỉ ẩn này ở giai đoạn đầu phát triển.

Cho mỗi rò rỉ được phát hiện, LeakCanary cung cấp mô tả và khuyến nghị sửa chữa. Phiên bản 2.14 đã thêm tích hợp với Android Lint — thư viện có thể tự động tạo tác vụ trong trình theo dõi vấn đề khi phát hiện rò rỉ trong CI.

Câu hỏi thường gặp

Có cần xóa LeakCanary khỏi APK phát hành không?

Có, nhất định phải làm. LeakCanary được thêm qua debugImplementation trong build.gradle, tự động loại trừ nó khỏi bản build phát hành. Nếu dùng implementation, thư viện sẽ được đưa vào APK phát hành và hiển thị rò rỉ cho người dùng cuối — điều này không thể chấp nhận được.

LeakCanary có làm chậm ứng dụng không?

Tác động đến hiệu suất là tối thiểu. LeakCanary chỉ kích hoạt sau onDestroy của thành phần và không can thiệp vào kết xuất giao diện hay xử lý chạm. Chi phí duy nhất là một khoảng dừng GC cưỡng chế ngắn (khoảng 100 ms) và ghi heap dump khi rò rỉ xảy ra (phần nhỏ của giây).

Làm cách nào để xuất báo cáo LeakCanary?

LeakCanary tự động lưu heap dump ở định dạng HPROF vào thư mục ứng dụng. Tệp có thể được xuất qua Android Studio: Device File Explorer → data/data/com.example/files/leakcanary/. Để xem, hãy mở tệp trong Memory Profiler qua Capture → Open Heap Dump.

LeakCanary có hoạt động với Jetpack Compose không?

Có, từ phiên bản 2.12 LeakCanary hỗ trợ đầy đủ Jetpack Compose. Thư viện theo dõi ngữ cảnh Composition và đối tượng State, tự động phát hiện rò rỉ trong các hàm Composable. Không cần cấu hình riêng — nó hoạt động ngay lập tức.

LeakCanary có thể đưa ra kết quả dương tính giả không?

Kết quả dương tính giả có thể xảy ra nhưng hiếm. LeakCanary sử dụng ba lần gọi GC trước khi tuyên bố rò rỉ, giúp loại bỏ hầu hết các trường hợp dương tính giả. Nếu bạn cho rằng một phát hiện là dương tính giả, hãy tạo IgnoredReference cho lớp cụ thể trong cấu hình.

Tổng kết

  • LeakCanary là thư viện tiêu chuẩn để phát hiện tự động rò rỉ bộ nhớ trong ứng dụng Android.
  • Thư viện sử dụng WeakReference và GC cưỡng chế để phát hiện đối tượng sống sót sau vòng đời của chúng.
  • Heap dump được phân tích bởi công cụ Shark tích hợp, xây dựng chuỗi tham chiếu từ GC Root đến đối tượng bị rò rỉ.
  • Cài đặt trong dự án hiện đại: một dòng trong build.gradle: debugImplementation.
  • LeakCanary 2.x được viết lại hoàn toàn bằng Kotlin và chạy nhanh hơn 5–10 lần so với phiên bản trước.
  • Rò rỉ phổ biến nhất: tham chiếu tĩnh đến Activity, trình lắng nghe chưa hủy đăng ký và Fragment trong BackStack.
  • Thêm LeakCanary vào bản build debug của mọi dự án — nó ngăn rò rỉ trên production.

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