Mỗi giây, ứng dụng Java, Python hay JavaScript của bạn tạo ra hàng triệu object. Phần lớn chúng chết ngay sau khi sinh, chỉ vài phần trăm sống đủ lâu để trở thành dữ liệu quan trọng. Generational garbage collection khai thác quy luật này để tăng tốc dọn bộ nhớ lên hàng chục lần so với cách quét toàn bộ heap.
Giả thuyết thế hệ: trẻ chết sớm, già sống dai
Năm 1984, David Ungar quan sát thấy rằng hơn 90% object trong chương trình Smalltalk bị hủy trước khi bộ thu gom rác kịp chạy lần thứ hai. Ông gọi đây là "generational hypothesis": object mới phân bổ gần như chắc chắn sẽ trở thành rác rất nhanh, trong khi object đã tồn tại qua nhiều chu kỳ thu gom thì có xu hướng tiếp tục tồn tại. Quy luật này đúng với mọi ngôn ngữ hiện đại, từ biến tạm trong vòng lặp cho đến chuỗi trung gian khi nối string.
Ba thế hệ: nursery, survivor, old
Bộ thu gom chia heap thành ba vùng riêng biệt. Generation 0 (nursery hay young generation) là nơi mọi object mới được cấp phát, chiếm khoảng 1-10% tổng heap. Khi nursery đầy, bộ thu gom chỉ quét vùng nhỏ này, tìm object còn sống bằng cách truy vết từ root set (biến toàn cục, stack frame, thanh ghi CPU). Object sống sót được sao chép sang Generation 1 (survivor space). Object trụ qua nhiều lần thu gom ở Gen 1 sẽ được "thăng cấp" lên Generation 2 (old generation hay tenured), vùng lớn nhất và ít bị quét nhất.
Remembered set: cầu nối giữa các thế hệ
Vấn đề lớn nhất của cách tiếp cận này là tham chiếu xuyên thế hệ. Khi một object già ở Gen 2 trỏ đến object trẻ ở Gen 0, bộ thu gom không thể bỏ qua tham chiếu đó khi quét nursery. Giải pháp là duy trì một "remembered set", danh sách các con trỏ từ thế hệ già sang thế hệ trẻ. Mỗi khi chương trình ghi giá trị vào một trường của object, một đoạn mã nhỏ gọi là "write barrier" kiểm tra xem phép gán có tạo tham chiếu xuyên thế hệ không. Nếu có, con trỏ được thêm vào remembered set. Chi phí write barrier rất nhỏ, chỉ vài lệnh máy cho mỗi phép gán, nhưng đổi lại bộ thu gom không cần quét toàn bộ heap.
Minor GC nhanh vì phạm vi nhỏ
Khi nursery đầy, bộ thu gom thực hiện "minor GC": quét Gen 0 bắt đầu từ root set cộng với remembered set, sao chép object sống sang Gen 1, rồi xóa toàn bộ nursery. Vì nursery nhỏ (thường 1-32 MB) và hơn 90% object trong đó đã chết, minor GC hoàn thành trong vài mili giây. So với "full GC" quét toàn bộ heap có thể mất hàng trăm mili giây, hiệu quả tăng vọt rõ rệt.
Ứng dụng thực tế: từ JVM đến V8
JVM của Java sử dụng G1 collector với nhiều region thay vì ba vùng cố định, nhưng nguyên lý thế hệ vẫn là nền tảng. V8 engine của Chrome chia heap thành young generation (semi-space) và old generation, chạy minor GC bằng thuật toán Cheney's copying collector. Python kết hợp reference counting với generational GC cho object có tham chiếu vòng. Khi SpaceX (SPCX) vừa IPO lịch sử với định giá 2,1 nghìn tỷ USD, hệ thống giao dịch của Nasdaq xử lý hơn 500 triệu cổ phiếu trong ngày đầu tiên, mỗi lệnh mua bán tạo ra hàng chục object tạm thời mà generational GC dọn sạch trong micro giây để đảm bảo độ trễ thấp.
Lần tới khi bạn thấy log "GC pause: 2ms", hãy nhớ rằng con số đó nhỏ nhờ một quan sát đơn giản từ hơn 40 năm trước: object trẻ chết sớm, và bộ thu gom chỉ cần tập trung vào nơi rác tích tụ nhiều nhất.





