Garbage Collection (GC): nó là gì, thuật toán và thu gom rác trong phát triển di động

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

Quản lý bộ nhớ tự động thông qua thu gom rác là cơ chế chính của nền tảng Android, dựa trên máy ảo ART. Theo Google Android Documentation, 2026, trình thu gom rác giải phóng nhà phát triển khỏi quản lý bộ nhớ thủ công, tự động xóa các đối tượng không còn tham chiếu. Nếu không có GC, mỗi lần cấp phát đối tượng sẽ yêu cầu gọi free hoặc delete một cách tường minh, điều này là bất khả thi trong hệ sinh thái Java với hàng triệu đối tượng mỗi giây.

Những điểm chính

  • Garbage Collection — cơ chế tự động giải phóng bộ nhớ bằng cách xóa các đối tượng không được sử dụng trong Java và Android
  • Thuật toán cơ bản — Mark-and-Sweep, Copying Collection và Generational Collection quyết định hiệu quả thu gom
  • ART và Dalvik — hai triển khai của máy ảo Android, nơi ART (Android Runtime) thay thế Dalvik từ Android 5.0
  • GC Pauses — tạm dừng thực thi ứng dụng trong quá trình thu gom — nguyên nhân chính gây jank và vấn đề hiệu suất
  • Tối ưu GC — giảm cấp phát, sử dụng nhóm đối tượng và chọn đúng loại thu thập giúp giảm tải cho trình thu gom

Garbage Collection (GC) là gì?

Garbage Collection (GC) là quá trình tự động phát hiện và giải phóng bộ nhớ bị chiếm giữ bởi các đối tượng mà chương trình không còn sử dụng nữa. Trong bối cảnh phát triển di động, GC được sử dụng trên nền tảng Android thông qua máy ảo ART, cũng như trong Java Virtual Machine tiêu chuẩn.

Không giống như các ngôn ngữ quản lý bộ nhớ thủ công (C, C++), nơi lập trình viên phải gọi free hoặc delete một cách tường minh, GC hoàn toàn đảm nhận nhiệm vụ theo dõi vòng đời của đối tượng. Nhà phát triển tạo đối tượng mới thông qua toán tử new, trong khi trình thu gom xác định khi nào một đối tượng trở nên không thể truy cập — nghĩa là không còn tham chiếu hoạt động nào đến nó.

Các chỉ số chính về hiệu quả của GC là thời gian tạm dừng (pause time) và thông lượng (throughput). Tạm dừng là khoảng thời gian thực thi ứng dụng bị dừng để tiến hành thu gom. Trong môi trường di động, thời gian tạm dừng lâu hơn 8–16 mili giây có thể nhận thấy dưới dạng khung hình bị rớt (jank).

Theo Google I/O 2019, ART trong Android 10 đã giảm thời gian tạm dừng GC điển hình xuống 2–4 ms, giảm 70% so với Dalvik trong Android 4.4. Tuy nhiên, quản lý bộ nhớ không đúng cách — cấp phát đối tượng thường xuyên trong vòng lặp, tạo các thể hiện tạm thời không cần thiết — vẫn là nguyên nhân chính gây ra vấn đề hiệu suất.

Trình thu gom rác hoạt động như thế nào: thuật toán cơ bản

Tất cả các triển khai GC trong Java và Android đều dựa trên một số thuật toán cơ bản được kết hợp để đạt được sự cân bằng giữa thời gian tạm dừng và mức độ triệt để của việc dọn dẹp. Hiểu các thuật toán này là cần thiết để viết mã thân thiện với GC.

Mark-and-Sweep

Mark-and-Sweep là thuật toán đơn giản nhất, hoạt động theo hai giai đoạn. Trong giai đoạn Mark, trình thu gom duyệt qua đồ thị đối tượng bắt đầu từ các tham chiếu gốc (root set) — biến cục bộ, trường tĩnh, ngăn xếp luồng. Mỗi đối tượng có thể truy cập được đánh dấu cờ sống. Trong giai đoạn Sweep, trình thu gom duyệt qua toàn bộ heap và giải phóng bộ nhớ của các đối tượng không được đánh dấu.

Nhược điểm là sự phân mảnh bộ nhớ: sau Sweep, các vùng trống xen kẽ với các vùng bị chiếm dụng, gây khó khăn cho việc cấp phát các đối tượng lớn. Trong các kịch bản di động, điều này rất quan trọng vì heap thường nhỏ (64–512 MB trên Android).

Copying Collection

Copying Collection chia heap thành hai nửa không gian (semi-spaces). Các đối tượng hoạt động được sao chép nhỏ gọn từ nửa không gian này sang nửa không gian kia, không có khoảng trống. Sau khi sao chép, nửa không gian cũ được tuyên bố là trống hoàn toàn. Thuật toán loại bỏ hoàn toàn sự phân mảnh, nhưng yêu cầu gấp đôi bộ nhớ.

Trong môi trường di động, Copying Collection được các trình thu gom thế hệ sử dụng để dọn dẹp nhanh các đối tượng trẻ, vốn theo thống kê sẽ chết sớm (giả thuyết thế hệ yếu).

Generational Collection

Generational Collection chia heap thành các thế hệ: Young Generation (đối tượng trẻ) và Old Generation (đối tượng già đã sống sót qua nhiều lần thu gom). Việc thu gom thế hệ trẻ (Minor GC) được thực hiện thường xuyên và nhanh chóng, vì hầu hết các đối tượng đều chết trẻ. Việc thu gom thế hệ già (Major GC hoặc Full GC) xảy ra ít thường xuyên hơn nhưng mất nhiều thời gian hơn.

java
// Minh họa GC thế hệ: các đối tượng trẻ chết nhanh
void processItems(List<Item> items) {
    List<Result> results = new ArrayList<>();       // sống trong suốt phương thức
    for (Item item : items) {
        Result r = new Result(item.getValue());    // chết ngay lập tức
        if (r.isValid()) {
            process(r);                               // r trở thành rác
        }
    }
    saveResults(results);                             // results chuyển sang Old Gen
}

Trong ví dụ này, các đối tượng Result được tạo bên trong vòng lặp và ngay lập tức trở thành rác — chúng là ứng viên lý tưởng cho Young GC. Đối tượng results sống lâu hơn và di chuyển đến Old Generation. Việc phân chia thế hệ cho phép Minor GC dọn dẹp các đối tượng trẻ trong mili giây mà không chạm vào heap cũ.

Garbage Collection trong Android: ART và Dalvik

Android đã phát triển từ Dalvik VM đến ART (Android Runtime), và việc triển khai GC là một trong những khác biệt chính giữa chúng. Hiểu kiến trúc GC trong Android giúp viết mã giảm thiểu thời gian tạm dừng trên các thiết bị thực.

Đặc điểmDalvik (đến 4.4)ART (5.0+)
Loại GCMark-and-Sweep với Concurrent MarkGenerational + Concurrent
Thời gian tạm dừng điển hình10–30 ms2–4 ms
Nén (Compaction)Không (chỉ phân mảnh tăng)Có (trong nền, không dừng ứng dụng)
Biên dịch AOTJIT (Just-In-Time)AOT + JIT (kết hợp)

Dalvik GC

Dalvik sử dụng kết hợp Mark-and-Sweep với một giai đoạn đồng thời. Concurrent Mark cho phép ứng dụng tiếp tục hoạt động trong quá trình duyệt đồ thị đối tượng, nhưng giai đoạn Sweep yêu cầu dừng tất cả các luồng (Stop-The-World). Trên các thiết bị có RAM nhỏ (512 MB — 1 GB), thời gian tạm dừng lên tới 30 ms, gây ra độ trễ đáng kể cho giao diện. Ngoài ra, Dalvik không nén heap, vì vậy sau khi sử dụng kéo dài, sự phân mảnh tăng lên và việc cấp phát các đối tượng lớn (ví dụ: Bitmap) có thể ném OutOfMemoryError ngay cả khi có đủ bộ nhớ trống tổng thể.

ART GC

ART (Android Runtime) đã giới thiệu một trình thu gom thế hệ với tính năng nén đồng thời. Heap được chia thành ba vùng: Young, Mature (tương tự Old Generation) và Large Object Space (cho các đối tượng lớn hơn 12 KB). Việc thu gom Young Region diễn ra song song mà không dừng các luồng trong hầu hết các trường hợp. Trong Android 10+, Concurrent Copying đã được giới thiệu — quá trình nén chạy trong luồng nền mà không cần Stop-The-World.

Nhờ kiến trúc của ART, thời gian tạm dừng GC điển hình đã giảm xuống còn 2–4 ms và trong các kịch bản có nhiều đối tượng trẻ — xuống còn 0.5–1 ms. Điều này cho phép các thiết bị Android duy trì 60 FPS ổn định ngay cả trong các hoạt động bộ nhớ tích cực.

Các loại trình thu gom rác trong Java

Trong hệ sinh thái Java, có một số triển khai GC, mỗi loại có cấu hình hiệu suất riêng. Đối với phát triển Android, sự lựa chọn bị giới hạn ở ART, nhưng kiến thức về Java GC rất hữu ích khi viết mã phía máy chủ cho các ứng dụng di động và khi phát triển với Kotlin Multiplatform.

Serial GC

Serial GC là trình thu gom đơn luồng với việc dừng hoàn toàn ứng dụng (Stop-The-World). Mỗi thao tác Mark, Sweep và Compact được thực hiện bởi một luồng. Hiệu suất thấp — không được sử dụng cho máy chủ di động. Chỉ phù hợp cho các ứng dụng nhỏ với heap lên đến 100 MB.

Parallel GC

Parallel GC (còn được gọi là Throughput Collector) sử dụng nhiều luồng cho tất cả các giai đoạn thu gom. Nó hướng đến thông lượng tối đa (throughput) — giảm thiểu thời gian dành cho GC so với thời gian chạy ứng dụng. Được kích hoạt qua cờ -XX:+UseParallelGC trong JVM.

G1 GC

G1 (Garbage-First) GC là trình thu gom mặc định trong Java 9+. Heap được chia thành các vùng 1–32 MB. G1 dự đoán thời gian tạm dừng và cố gắng duy trì trong giới hạn đã chỉ định (mặc định 200 ms). Ưu tiên: các vùng có nhiều rác nhất được dọn dẹp trước (do đó có tên). G1 hiệu quả cho máy chủ có heap lớn (4–64 GB) với thời gian tạm dừng có thể dự đoán.

java
// Kích hoạt G1 GC với thời gian tạm dừng mục tiêu 100 ms
// java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar app.jar

public class MemoryMonitor {
    private static final long THRESHOLD = 512 * 1024 * 1024; // 512 MB

    public void checkHeapUsage() {
        Runtime rt = Runtime.getRuntime();
        long used = rt.totalMemory() - rt.freeMemory();
        if (used > THRESHOLD) {
            System.out.println("Heap usage exceeded threshold: " + used);
            System.out.println("Consider reducing allocations");
        }
    }
}

Giám sát heap thông qua Runtime cho phép phát hiện rò rỉ bộ nhớ ở giai đoạn sớm. Nếu used vượt quá 80% heap tối đa trong hoạt động ổn định — đây là tín hiệu của rò rỉ tiềm ẩn hoặc tiêu thụ bộ nhớ quá mức của ứng dụng.

Vấn đề GC và tối ưu bộ nhớ trong ứng dụng di động

Ngay cả ART GC hiện đại cũng không giải quyết được tất cả vấn đề — sử dụng bộ nhớ không đúng cách vẫn là nguyên nhân chính gây ra jank và ANR (Application Not Responding). Hãy xem xét các kịch bản chính và phương pháp tối ưu hóa.

GC Pauses và Jank

GC Pauses — các luồng ứng dụng bị dừng trong quá trình thu gom. Trên màn hình, điều này biểu hiện dưới dạng khung hình bị rớt, khi thời gian giữa hai khung hình vượt quá 16.6 ms (60 FPS). Nếu GC kéo dài 30 ms, chỉ một khung hình được vẽ thay vì hai — người dùng thấy giật lag ở giao diện.

Nguyên nhân chính của thời gian tạm dừng dài: số lượng lớn đối tượng sống trong Old Generation, phân mảnh heap, Full GC thường xuyên. Để chẩn đoán, sử dụng Android Studio Profiler và systrace.

Giảm tải GC

Quy tắc chính của mã thân thiện với GC là giảm thiểu số lượng đối tượng được cấp phát. Mỗi đối tượng mới không chỉ yêu cầu cấp phát bộ nhớ mà còn cả việc thu gom sau đó. Ngay cả khi GC nhanh, 1000 lần cấp phát thêm mỗi giây sẽ tạo ra 1000 lần kiểm tra cho trình thu gom.

  • Tránh tạo đối tượng trong vòng lặp — di chuyển việc tạo ra ngoài vòng lặp, tái sử dụng biến cục bộ
  • Sử dụng nhóm đối tượng — cho Bitmap, byte[] và các cấu trúc nặng khác, sử dụng Object Pool hoặc RecyclerView.ViewHolder
  • Ưu tiên kiểu nguyên thủy — int thay vì Integer, float thay vì Float tránh autoboxing
  • Sử dụng SparseArray — thay vì HashMap<Integer, V>, Android SDK cung cấp SparseArray, LongSparseArray hoạt động với kiểu nguyên thủy
  • StringBuilder thay vì nối chuỗi — mỗi lần nối chuỗi tạo ra một đối tượng String mới

Rò rỉ bộ nhớ

Rò rỉ bộ nhớ xảy ra khi một đối tượng vẫn có thể truy cập được mặc dù không còn cần thiết. GC không thể xóa đối tượng như vậy và bộ nhớ dần cạn kiệt. Nguyên nhân điển hình: người nghe chưa được hủy đăng ký, tham chiếu tĩnh đến Activity, lớp ẩn danh nắm giữ ngữ cảnh bên ngoài và Cursor/InputStream chưa đóng.

java
// Rò rỉ bộ nhớ: lớp ẩn danh giữ tham chiếu đến Activity
public void startTask() {
    new Thread(new Runnable() {                    // ngầm giữ this (Activity)
        @Override
        public void run() {
            // thao tác dài...
            System.out.println("Done");
        }
    }).start();
}

// Khắc phục: lớp tĩnh lồng nhau + WeakReference
private static class TaskRunnable implements Runnable {
    private WeakReference<Activity> activityRef;

    TaskRunnable(Activity activity) {
        this.activityRef = new WeakReference<>(activity);
    }

    @Override
    public void run() {
        Activity act = activityRef.get();
        if (act != null) {
            // làm việc an toàn với Activity
        }
    }
}

Trong ví dụ này, Runnable ẩn danh nắm giữ một tham chiếu ngầm đến Activity. Khi luồng còn sống — Activity không thể được GC thu gom, ngay cả khi người dùng đã đóng màn hình. Việc sửa chữa với WeakReference + lớp tĩnh phá vỡ chuỗi này và cho phép Activity được giải phóng.

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

GC trong Android khác với GC trong Java như thế nào?

GC trong Android (ART) là trình thu gom thế hệ với tính năng nén đồng thời, được tối ưu cho thiết bị di động có bộ nhớ hạn chế. Java GC (G1, ZGC) là các trình thu gom phía máy chủ với heap lớn và thời gian tạm dừng có thể dự đoán. ART GC không sử dụng cờ JVM — tất cả việc điều chỉnh được thực hiện tự động ở cấp độ HĐH.

Stop-The-World trong GC là gì?

Stop-The-World là thời điểm trình thu gom tạm dừng tất cả các luồng ứng dụng để duyệt đồ thị đối tượng hoặc giải phóng bộ nhớ một cách an toàn. STW càng lâu, jank càng dễ nhận thấy. ART đã giảm thời gian STW điển hình xuống 2–4 ms nhờ kiến trúc thế hệ của nó.

Làm thế nào để phát hiện rò rỉ bộ nhớ trong Android?

Sử dụng Android Studio Memory Profiler — nó hiển thị sự tăng trưởng của heap, số lượng cấp phát và cho phép thực hiện Heap Dump. Để phân tích sâu, sử dụng LeakCanary — thư viện tự động phát hiện rò rỉ và hiển thị chuỗi tham chiếu ngăn cản việc thu gom GC.

Khi nào Full GC xảy ra và tại sao nó nguy hiểm?

Full GC là quá trình thu gom hoàn toàn tất cả các thế hệ heap, bao gồm cả Old Generation. Trong các ứng dụng di động, Full GC có thể kéo dài 50–200 ms, gây ra jank hoặc ANR đáng kể. Nguyên nhân chính: phân mảnh heap, rò rỉ bộ nhớ, vượt quá ngưỡng Old Generation.

Kotlin giúp tránh rò rỉ bộ nhớ như thế nào?

Kotlin cung cấp coroutine với tính đồng thời có cấu trúc — việc hủy phạm vi tự động hủy tất cả các coroutine con, ngăn chặn rò rỉ. Kotlin cũng có delegate lazy cho khởi tạo trễ và các hàm phạm vi giúp giảm số lượng đối tượng tạm thời.

Tổng kết

  • Garbage Collection — quản lý bộ nhớ tự động bằng cách xóa các đối tượng không thể truy cập, nền tảng của Android Runtime
  • Mark-and-Sweep — thuật toán cơ bản với thu gom hai giai đoạn, bị phân mảnh heap
  • Copying Collection — loại bỏ phân mảnh bằng cách sao chép đối tượng sống vào một nửa không gian nhỏ gọn
  • Generational GC — chia heap thành các thế hệ (Young/Old), tăng tốc thu gom các đối tượng trẻ tồn tại ngắn
  • ART trong Android — trình thu gom thế hệ với nén đồng thời và thời gian tạm dừng 2–4 ms, thay thế Dalvik trong Android 5.0
  • Tối ưu GC — giảm cấp phát, nhóm đối tượng, kiểu nguyên thủy thay vì wrapper và SparseArray thay vì HashMap giúp giảm tải cho trình thu gom
  • Chẩn đoán — Android Studio Profiler, systrace và LeakCanary là các công cụ chính để xác định vấn đề bộ nhớ

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