Bộ nhớ/Module 4 — Tổng kết & cheat sheet
26/26
Bài 26 / 26~12 phútQuản lý bộ nhớ ngôn ngữ bậc caoMiễn phí lượt xem

Module 4 — Tổng kết & cheat sheet

Recap quản lý bộ nhớ ngôn ngữ bậc cao: manual vs GC, ref-counting vs tracing, logical leak, AoS→SoA. Cheat sheet, glossary, pitfall tổng hợp.

TL;DR: Module 4 đi từ câu hỏi "ai dọn rác" đến kỹ năng chẩn đoán leak và tối ưu cache. Bạn biết cấp phát thủ công đòi hỏi kiểm soát chặt (malloc/free) còn GC tự động nhưng không phải viên đạn bạc; biết ref-counting đơn giản nhưng chết ở cycle trong khi tracing GC mạnh hơn với cái giá stop-the-world pause; biết logical leak là thứ GC không cứu được vì object vẫn reachable; và cuối cùng đo trực tiếp rằng SoA thực sự nhanh hơn AoS khi vòng lặp chỉ dùng ít field. Đây là trang để bookmark.

Đã đi qua những gì

Module bắt đầu từ hai triết lý đối lập: C đặt trách nhiệm dọn dẹp vào tay lập trình viên (malloc/free) — kiểm soát tuyệt đối, nhưng một lỗi nhỏ dẫn tới leak, double free, dangling pointer. Ngôn ngữ bậc cao (Java, Go, Python) giao việc đó cho runtime GC — an toàn hơn, code đơn giản hơn, nhưng không miễn phí hoàn toàn.

Bạn đi sâu vào hai cơ chế GC chính: ref-counting (đếm bao nhiêu thứ đang giữ object, thu khi = 0 — đơn giản, real-time, nhưng chết ở reference cycle) và tracing GC (duyệt đồ thị reachability từ root set, mark-and-sweep — mạnh hơn, thu được cycle, nhưng gây stop-the-world pause). Generational GC kết hợp cả hai tư tưởng: chia heap theo tuổi, GC thường xuyên vùng trẻ, ít thường xuyên vùng già.

Rồi bạn học điều quan trọng nhất về GC: nó không cứu được logical leak — object reachable nhưng không bao giờ dùng nữa. Static collection không clear, listener không unregister, ThreadLocal không remove, cache không có eviction — đây là những "lỗi logic" mà GC nhìn thấy một tham chiếu hợp lệ và không dám thu. Triệu chứng: heap tăng dần, GC chạy nhiều mà không giảm, cuối cùng OOM.

Capstone kết nối lại với Module 2: bạn tự đo rằng refactor AoS sang SoA giảm thời gian vòng lặp 2–5 lần nhờ spatial locality — đây là minh chứng sống rằng bố cục dữ liệu quyết định hiệu năng, không phải chỉ thuật toán.

mindmap
  root(("Quan ly<br/>bo nho"))
    Manual vs GC
      malloc free trong C
      Leak double-free dangling pointer
      GC tự động thu reachable
      Tradeoff an toan vs kiem soat
    Co che GC
      Ref-counting dem tham chieu
      Chet o reference cycle
      Tracing GC mark-and-sweep
      Generational young old gen
      Stop-the-world pause
    Logical leak
      GC chi thu unreachable
      Reachable nhung vo dung
      Static collection listener ThreadLocal
      Heap dump dominator tree
    Cache va layout
      AoS pointer chasing field thua
      SoA field lien tuc 100% huu ich
      Do truoc-sau nanoTime JMH
      SIMD mo duong tu SoA

🗺️ Cheat sheet

Khái niệmCốt lõiPitfall / khi nào dùng
Manual allocationmalloc cấp phát, free giải phóng; lập trình viên kiểm soát hoàn toànQuên free = leak; free 2 lần = UB; dùng sau free = dangling pointer
Memory leak (C)malloc mà không free; pointer tới vùng bị mấtHàm return sớm trước free; exception path không chạy cleanup
Double freefree(ptr) gọi 2 lần → UB, crash, lỗ hổng bảo mậtSet ptr = NULL sau mỗi free
Dangling pointerCon trỏ trỏ vào vùng đã free → đọc/ghi UBNull-ify pointer sau free; tránh trả con trỏ tới biến cục bộ
Garbage collectionRuntime tự thu object không còn reachable từ root setKhông phải zero-cost; GC pause ảnh hưởng latency
Root setStack + static field + thanh ghi + JNI refs — neo ban đầu của reachability graphObject không reachable từ đây sẽ bị thu
Ref-countingĐếm số tham chiếu; thu khi = 0 — real-time, không pauseCycle A→B→A: cả hai count vẫn vượt 0, không bao giờ thu
Reference cycleA giữ B, B giữ A → ref-count không về 0Python dùng cycle detector bổ sung; Go/Java dùng tracing GC
Tracing GCDuyệt từ root set, mark reachable, sweep/compact restStop-the-world pause; tuning heap size quan trọng
Mark-and-sweepMark phase: đánh dấu reachable; sweep phase: thu unmarkedCó thể gây heap fragmentation nếu không compact
Generational GCYoung gen (minor GC thường): most object die young; old gen (major GC hiếm)Promotion premature: object non-long-lived vào old gen → major GC nhiều hơn
Stop-the-worldGC tạm dừng mọi thread để scan heapVấn đề với latency-sensitive app; dùng ZGC/Shenandoah cho low-pause
Logical leakObject reachable nhưng không bao giờ dùng lại — GC không thuStatic cache, unremoved listener, ThreadLocal không reset
WeakReferenceGC thu khi không còn strong ref, dù còn WeakRefCache kiểu "tồn tại khi còn ai dùng key"; WeakHashMap
SoftReferenceGC thu khi memory pressure caoCache "giữ nếu đủ RAM, nhả nếu thiếu"; không đoán được timing
AoSMảng các struct đầy đủ field; truy cập nhiều field/phần tử hợpVòng lặp dùng ít field → kéo field thừa vào cache line
SoAMỗi field một mảng riêng; vòng lặp ít field nhanh hơnPhá đóng gói; dễ lệch chỉ số; chỉ dùng khi profiler xác nhận

📖 Glossary module

Thuật ngữĐịnh nghĩa 1 câu
Manual allocationCấp phát và giải phóng bộ nhớ thủ công (malloc/free trong C) — lập trình viên kiểm soát hoàn toàn
malloc / freeHàm C cấp phát (malloc) và giải phóng (free) vùng nhớ trên heap
Memory leakBộ nhớ được cấp phát nhưng không bao giờ được giải phóng — tích luỹ cho đến khi OOM
Double freeGọi free hai lần trên cùng một con trỏ — undefined behavior trong C
Use-after-freeTruy cập vùng nhớ đã bị free — undefined behavior, nguồn gốc nhiều lỗ hổng bảo mật
Dangling pointerCon trỏ trỏ vào vùng nhớ đã được giải phóng hoặc ra ngoài scope
Garbage collectionCơ chế tự động thu hồi bộ nhớ không còn được dùng bởi runtime của ngôn ngữ
ReachabilityKhả năng truy cập tới một object từ root set qua chuỗi tham chiếu — tiêu chí GC quyết định sống/chết
Root setTập hợp các "neo" luôn được coi là sống: stack frame, static field, thanh ghi, JNI refs
Reference countingKỹ thuật GC đếm số tham chiếu tới mỗi object; thu khi đếm về 0
Reference cycleHai hoặc nhiều object giữ tham chiếu lẫn nhau tạo vòng kín — ref-counting không thu được
Tracing GCKỹ thuật GC duyệt đồ thị tham chiếu từ root set, đánh dấu object sống, thu phần còn lại
Mark-and-sweepThuật toán tracing GC gồm hai pha: mark (đánh dấu reachable) và sweep (thu unreachable)
Generational GCTối ưu hoá GC dựa trên "weak generational hypothesis" — hầu hết object chết trẻ; chia young/old gen
Young gen / Old genHai vùng heap trong generational GC: young gen cho object mới (minor GC thường), old gen cho object sống lâu (major GC hiếm)
Stop-the-world pauseKhoảng thời gian GC tạm dừng mọi thread ứng dụng để thực hiện mark/compact phase
Logical leakObject còn reachable (GC không thu) nhưng không bao giờ được dùng lại về mặt logic
WeakReferenceTham chiếu yếu cho phép GC thu object khi không còn strong reference nào
AoSArray of Structs — mảng các bản ghi, mỗi bản ghi gói đủ field liền nhau
SoAStruct of Arrays — mỗi field thành một mảng riêng, phần tử cùng field nằm liền nhau

⚠️ Pitfall tổng hợp

1. Quên free trên mọi code path trong C:

// SAI: early return truoc khi free
char* process(int n) {
    char* buf = malloc(n);
    if (n > MAX) return NULL;   // LEAK: buf chua duoc free
    // ... xu ly ...
    free(buf);
    return result;
}

// DUNG: free tren moi path
char* process(int n) {
    char* buf = malloc(n);
    if (n > MAX) {
        free(buf);              // cleanup truoc khi return
        return NULL;
    }
    // ... xu ly ...
    free(buf);
    return result;
}

2. Tin "có GC = không leak":

// SAI: static cache tang mai khong clear
static final Map<String, byte[]> cache = new HashMap<>();

public void handle(String key, byte[] data) {
    cache.put(key, data);       // put nhung khong co remove/evict
}
// Sau 1 trieu request: 1 trieu entry trong heap, OOM

// DUNG: bounded cache voi eviction
static final Cache<String, byte[]> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(5, TimeUnit.MINUTES)
    .build();

3. ThreadLocal trong thread pool — quên remove:

// SAI: set khong remove
threadLocal.set(requestContext);
doWork();
// thread pool tai dung thread -> context cu van o day

// DUNG: remove trong finally
threadLocal.set(requestContext);
try {
    doWork();
} finally {
    threadLocal.remove();       // dam bao ca khi exception
}

4. AoS khi vòng lặp chỉ dùng ít field:

// SAI (hieu nang): AoS khi hot loop chi dung x, y
for (Particle p : particles) {
    p.x += p.vx * dt;           // keo ca mass, charge, color vao cache
    p.y += p.vy * dt;
}

// DUNG: SoA khi hot loop it field, du lieu lon hon cache
for (int i = 0; i < n; i++) {
    xs[i] += vxs[i] * dt;       // cache line toan x, toan vx
    ys[i] += vys[i] * dt;
}

5. Ref-counting với reference cycle:

# Python ref-counting bi bat bai boi cycle
class Node:
    def __init__(self):
        self.sibling = None

a = Node()
b = Node()
a.sibling = b   # a giu b
b.sibling = a   # b giu a -- CYCLE
del a
del b
# ref count cua ca hai van = 1 (giu lan nhau)
# Python cycle detector xu ly duoc, nhung chi chay dinh ky
# -- khong phai immediate reclaim nhu thong thuong

✅ Self-assessment

Bạn đã đạt module này nếu trả lời được:

  • Compare được cấp phát thủ công (malloc/free) với garbage collection tự động về an toàn và chi phí — nêu được ít nhất 2 bug chỉ có ở manual, và 1 chi phí GC không miễn phí.
  • Explain được ref-counting và tracing GC hoạt động thế nào, ưu nhược điểm mỗi cách — giải thích được vì sao ref-counting chết ở cycle và tracing GC cần stop-the-world.
  • Diagnose được memory leak trong ngôn ngữ có GC và giải thích vì sao GC không cứu được — nêu được 3 pattern phổ biến và cách dùng heap dump + dominator tree để tìm thủ phạm.
  • Refactor được chương trình dùng AoS sang SoA và đo cải thiện trước-sau — chạy được cả hai phiên bản, giải thích được vì sao SoA nhanh hơn dựa trên cache line và spatial locality.

🚀 What's next

Bạn vừa hoàn thành khoá Bộ nhớ (cs-memory) — course tier 2 của track Máy tính cho Lập trình viên.

Nhìn lại hành trình 4 module:

  • Module 1 — Mô hình bộ nhớ chương trình: stack, heap, static, con trỏ — bố cục của mọi chương trình đang chạy.
  • Module 2 — Cache & memory hierarchy: tại sao truy cập dữ liệu nhanh hay chậm quyết định hiệu năng; cache line, locality, AoS/SoA, false sharing.
  • Module 3 — Bộ nhớ ảo: mỗi tiến trình thấy không gian địa chỉ ảo riêng; paging, page table, MMU, TLB, page fault, swap.
  • Module 4 — Quản lý bộ nhớ ngôn ngữ: ai dọn rác, dọn thế nào, và khi nào GC không đủ.

Bốn module này cho bạn mechanical sympathy — khả năng nhìn qua lớp trừu tượng ngôn ngữ và thấy máy tính đang làm gì thật sự với bộ nhớ. Đây là nền tảng để đọc code và đoán được "chỗ này có thể chậm vì cache miss", "chỗ này có thể OOM vì logical leak", "chỗ này GC pause có thể gây latency spike".

Tiếp theo: khoá tier 3 của track chưa sẵn sàng. Trong thời gian chờ, hãy củng cố bằng cách:

  • Chạy lại capstone bài 04 với N = 10_000_000 và đo bằng perf stat nếu bạn dùng Linux.
  • Áp dụng heap dump (jmap + Eclipse MAT) vào một ứng dụng Java bạn đang develop — thử tìm dominator lớn nhất.
  • Đọc "What Every Programmer Should Know About Memory" (Drepper) chương 3–5 — cùng topic nhưng sâu hơn và từ góc nhìn C/Linux.

Khám phá thêm: Tất cả khoá học

📚 Tài liệu mở rộng

  • "The Garbage Collection Handbook" (Richard Jones, Antony Hosking, Eliot Moss, 2011) — cuốn sách toàn diện nhất về GC algorithms: ref-counting, tracing, generational, incremental, concurrent, real-time. Đọc chương 1–3 cho nền tảng; chương 6–7 cho generational và concurrent GC của JVM.
  • OSTEP — "Operating Systems: Three Easy Pieces" (Arpaci-Dusseau) — miễn phí tại https://pages.cs.wisc.edu/~remzi/OSTEP/ — chương "Address Spaces" và "Free Space Management" liên hệ trực tiếp với manual allocation và virtual memory ở Module 3.
  • "What Every Programmer Should Know About Memory" (Ulrich Drepper) — chương 5–7 đào sâu AoS/SoA, NUMA, prefetching — đọc sau khi xong Module 2 và capstone này để thấy bức tranh đầy đủ hơn.
  • HotSpot GC documentation (Oracle) — tài liệu chính thức về G1GC và ZGC (hai GC của HotSpot): tuning, pause time goals, heap sizing. Tìm tại https://docs.oracle.com/en/java/javase/21/gctuning/ (Java 21).

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên