Quản lý Bộ nhớ và Cơ chế Garbage Collection trong JavaScript

2.1 Giới thiệu

Trong JavaScript, quản lý bộ nhớ là quá trình kiểm soát việc phân bổ và thu hồi bộ nhớ một cách hiệu quả để đảm bảo ứng dụng chạy mượt mà và không bị rò rỉ bộ nhớ (memory leak). Mặc dù JavaScript là ngôn ngữ "managed" — nghĩa là nó có cơ chế tự động quản lý bộ nhớ thông qua garbage collector — nhưng hiểu rõ về cách hoạt động của bộ nhớ sẽ giúp bạn viết mã hiệu quả, tối ưu và tránh lỗi khó debug.

2.2 Phân bổ bộ nhớ (Memory Allocation)

Bộ nhớ được cấp phát khi:

  • Bạn khai báo biến (let, const, var)

  • Tạo đối tượng ({}), mảng ([]), hoặc hàm (function(){})

  • Sử dụng closure hoặc class

  • Thực hiện thao tác DOM tạo ra element trong bộ nhớ

Ví dụ:

const name = "Tài Titans"; // Bộ nhớ được cấp phát cho chuỗi
const user = { id: 1, name: "Tài" }; // Đối tượng user cũng chiếm bộ nhớ

2.3 Giải phóng bộ nhớ (Memory Release)

Khi không còn tham chiếu nào trỏ tới một vùng bộ nhớ, garbage collector sẽ thu hồi vùng nhớ đó. Điều này giúp tránh hiện tượng "bloating" (phình to bộ nhớ).

Cách nhận biết vùng nhớ không còn được sử dụng:

  • Biến được gán null hoặc undefined

  • Scope kết thúc (biến trong hàm hoặc block bị loại bỏ)

let temp = { a: 1 };
temp = null; // đối tượng ban đầu sẽ được giải phóng nếu không còn tham chiếu nào khác

2.4 Garbage Collector hoạt động như thế nào?

JavaScript sử dụng nhiều thuật toán để xử lý việc thu hồi bộ nhớ, phổ biến nhất là Mark-and-Sweep.

1. Mark-and-Sweep

  • Bộ thu gom sẽ đánh dấu (mark) tất cả các object có thể truy cập từ root (global object, stack, closure,...) như là "active"

  • Sau đó, các object không được đánh dấu sẽ bị dọn dẹp (sweep)

2. Reference Counting (ít dùng hơn)

  • Đếm số lượng tham chiếu đến một object

  • Khi không còn tham chiếu nào → thu hồi bộ nhớ

Lưu ý: Reference counting dễ gây memory leak với cấu trúc vòng (circular reference)

function createCycle() {
  const obj1 = {};
  const obj2 = {};
  obj1.ref = obj2;
  obj2.ref = obj1;
  return [obj1, obj2];
}

2.5 Các kiểu vùng nhớ quan trọng trong JS

- Stack:

  • Dùng cho primitive types và scope variables

  • Quản lý theo kiểu LIFO (Last-In First-Out)

- Heap:

  • Dùng cho object, array, function, closure

  • Dữ liệu lưu trong heap có thể sống lâu hơn block hiện tại

2.6 Rò rỉ bộ nhớ (Memory Leak)

Khi bộ nhớ không được giải phóng đúng cách, có thể dẫn đến ứng dụng chậm, crash, hoặc tiêu tốn tài nguyên không cần thiết.

Các nguyên nhân phổ biến:

  • Global variable không được xóa

  • Closure giữ tham chiếu đến biến cũ

  • DOM reference bị giữ lại trong JS

  • Interval hoặc Timeout không clear

let leak = [];
function forever() {
  leak.push(new Array(1000000).fill("leak"));
  setTimeout(forever, 1000);
}
forever();

2.7 Các kỹ thuật phòng tránh Memory Leak

  • Sử dụng let/const thay vì var để tránh biến toàn cục

  • Clear setInterval, setTimeout, event listener khi không cần dùng nữa

  • Tránh giữ tham chiếu không cần thiết trong closure

  • Kiểm tra và debug bằng công cụ như Chrome DevTools → tab Memory

2.8 Công cụ hỗ trợ quản lý bộ nhớ

  • Chrome DevTools → Performance → Memory snapshot

  • Performance profiler để kiểm tra heap usage, garbage collection

  • Heap snapshot comparison giúp phát hiện memory leak theo thời gian

2.9 Kết luận

Hiểu rõ về quản lý bộ nhớ và cơ chế Garbage Collection giúp lập trình viên JavaScript viết ra các ứng dụng mượt mà, tiết kiệm tài nguyên và dễ bảo trì. Đây là yếu tố then chốt mà một Senior Developer cần nắm vững để xây dựng những hệ thống lớn, hoạt động lâu dài và hiệu quả.

Last updated