Bình Phước, 6/4/2025
NÍ HẢO AE, 2 tuần trước là thật bận rộn vì mình phải chuyển trọ nên bỏ bê chuyện tự học. An cư lập nghiệp mà, phải ổn định chỗ ở thì đầu óc mới tập trung làm việc được. Ước gì có tiền mua hẳn 1 cái nhà dưới quê, về sống với bố mẹ, khỏi lo chuyển trọ nữa. Hehe. Thôi tính reset lại nhật ký he.
I - Garbage là gì?
Garbage là 1 vùng nhớ được khởi tạo nhưng không có con trỏ nào trỏ tới nó. Garbage là một vùng nhớ vô dụng, không được sử dụng. Dần dần nó sẽ dẫn đến hiện tượng memory leak.
Ví dụ với C++:
void someFunc() { int* ptr = new int(10); // Tạo một vùng nhớ mới, biến ptr sẽ trỏ vào vùng nhớ đó ptr = null; // mất tham chiếu từ ptr, tạo ra một garbage
}
Ví dụ với Dart:
void someFunc() { var obj = Object(); obj = null; // Giờ Object là 1 garbage vì obj đã mất tham chiếu đến nó.
}
Ví dụ với Go:
func someFunc() { ptr := new(int) *ptr = 10 ptr = null // mất tham chiếu từ ptr, tạo ra một garbage
}
II - Garbage collector (viết tắt: GC)
Quản lý pointer trong C++
Trong C++ không có khái niệm garbage chính thức cũng như built-in GC bởi vì ta phải tự tay quản lý bộ nhớ cho các pointer. Ví dụ:
void someFunc() { int* ptr = new int(10); // Tạo một vùng nhớ mới, biến ptr sẽ trỏ vào vùng nhớ đó delete ptr; // Giải phóng bộ nhớ ptr = null; // Đặt về nullptr để tránh dangling pointer
}
Đây là cách cơ bản nhất để quản lý bộ nhớ trong C++. Nội dung cũng khá đơn giản:
- Có
new
thì phải códelete
tương ứng. delete
xong thì phải đặt pointer vềnull
để tránh trường hợp dangling pointer (vì bài viết này đang tập trung vào garbage nên mình xin skip phần dangling này nhé).
Các ngôn ngữ lập trình cấp cao hơn đều được tích hợp sẵn trình thu gom rác (built-in GC) và 99.99% công việc thường ngày của chúng ta sẽ chẳng cần quan tâm rằng GC là gì và nó sẽ hoạt động như thế nào. Tuy nhiên, nếu bạn có hứng thú với những thứ "under the hood", bạn nên tìm hiểu sâu hơn vì chúng rất thú vị đấy! Có khi còn pass được 1 câu hỏi phỏng vấn nữa chứ!
GC thường hoạt động như thế nào?
1. Nguyên lý cơ bản
GC hoạt động dựa trên 2 việc (nghe có vẻ là đơn giản) như sau:
- Bước 1. Xác định garbage.
- Bước 2. Thu hồi bộ nhớ (gom rác).
2. Các thuật toán GC phổ biến
Mark & Sweep:
- Mark: Bắt đầu từ root (như các biến global, trong stack memory, thread...), duyệt đồ thị tới tất cả mọi nơi. Khi duyệt đồ thị, những object nào có đường đi tới thì được đánh dấu là còn sống.
- Sweep: Duyệt qua toàn bộ heap memory, những object nào không được đánh giá còn sống, nghĩa là đó là garbage và cần phải bị loại bỏ.
- Mark & Sweep rất cơ bản & dễ triển khai. Tuy nhiên, như mô tả phía trên, Mark & Sweep tuy đơn giản nhưng sẽ có thể gây chậm chạp. Nó phù hợp hơn với các hệ thống server.
Generational GC:
- Dựa trên ý tưởng rằng, một số đối tượng sẽ có vòng đời ngắn (short-lived), một số đối tượng sẽ sống lâu hơn (long-lived).
- Generational GC sẽ chia heap ra làm 2 vùng:
- Young generation: Chứa các đối tượng mới được khởi tạo.
- Old generation: Chứa các đối tượng đã nhiều lần sống sót qua nhiều làm gom rác.
- GC sẽ chủ yếu quét & gom rác trong Young generation, vì nơi đây tạo ra nhiều rác nhất, chi phí xử lý cũng nhanh & ít ảnh hưởng hiệu năng. Flutter/Dart cũng sử dụng kiểu GC này.
Mark & Sweep là một cách thực thi rất cơ bản, trong khi ý tưởng của Generational GC là một trong những cách phổ biến nhất & tốt nhất hiện nay. Ngoài ra còn một số kiểu gom rác khác như Reference Counting nhưng mình không code ngôn ngữ nào xài nó cả và có vẻ nó cũng không tốt bằng Generational nên thôi skip nhá 😃.
III - GC trong Dart
Dart dùng kỹ thuật Generational GC. Bạn có thể đọc thêm về ý tưởng của Generational GC ở phía trên.
Trong quá trình hoạt động, Flutter luôn tạo ra rất nhiều widget + state nên sẽ gây ra rât nhiều garbage. Việc gom rác nhanh chóng và thường xuyên ở Young generation, kết hợp với một số kỹ thuật concurrency và schedule đúng cách sẽ giúp Flutter app luôn mượt mà và không bị drop FPS.
Đối với Young generation
Ở Young generation, Dart sử dụng kỹ thuật gọi là Semi-space GC. Heap sẽ được chia làm 2 nửa gọi là From-space và To-space. Bạn hãy quan sát hình bên dưới.
- Khi một object mới xuất hiện, nó sẽ được khởi tạo trong vùng nhớ nằm ở From-space (2).
- Khi nửa From-space đầy (3), GC sẽ kiểm tra xem những object nào còn sống (4), và di chuyển chúng tới nửa To-space (5).
- Xoá sạch những garbage còn tồn tại trong From-space, và đổi vai trò của 2 nửa cho nhau (6).
Source ảnh: Flutter: Don’t Fear the Garbage Collector
Đối với Old generation
Sử dụng Mark-Sweep hoặc các biến thể như Mark-Compact.
IV - GC trong Go
// To be continue
Tham khảo
(1) Matt Sullivan. Flutter: Don’t Fear the Garbage Collector. URL: https://medium.com/flutter/flutter-dont-fear-the-garbage-collector-d69b3ff1ca30. Last accessed: 6/4/2025.
(2) Nima Farzin. Garbage Collection in Dart and Its Implications in Flutter. URL: https://nimafarzin-pr.medium.com/garbage-collection-in-dart-and-its-implications-in-flutter-aef9de51bfc4. Last accessed: 1/4/2025.
(3) AI Chatbots.