Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Biến JavaScript được cấp phát bộ nhớ như thế nào?

JavaScript quản lý bộ nhớ tự động (Garbage Collected). Cách cấp phát khác với C/C++ (manual malloc/free) hoặc Java (heap cho object, stack cho primitive).

Mô hình bộ nhớ JS

1. Call Stack (Ngăn xếp gọi) – Cho Primitive & Reference

  • Lưu trữ: Primitive values (number, string, boolean, null, undefined, symbol, bigint) và References (con trỏ) trỏ đến object trong Heap.
  • Cấp phát: Tự động khi vào scope (function/block).
  • Giải phóng: Tự động khi ra khỏi scope (pop stack frame).
  • Kích thước nhỏ, cố định, truy xuất cực nhanh.

2. Heap (Đống) – Cho Object

  • Lưu trữ: Object, Array, Function, Date, RegExp, Map, Set... – dữ liệu kích thước động, phức tạp.
  • Cấp phát: Khi tạo object (new, {}, [], function() {}).
  • Giải phóng: Garbage Collector (GC) tự động dọn khi không còn reference nào trỏ tới (unreachable).
  • Kích thước lớn, động, truy xuất gián tiếp qua reference.

Minh họa: Biến primitive vs Object


// Primitive: Lưu trực tiếp trên Stack
let x = 10;      // Stack: x → 10
let y = x;       // Stack: y → 10 (copy giá trị)
y = 20;          // Stack: y → 20, x vẫn là 10

// Object: Lưu reference trên Stack, Object trên Heap
let obj1 = { value: 10 }; 
// Stack: obj1 → ref#123
// Heap:  ref#123 → { value: 10 }

let obj2 = obj1;          // Stack: obj2 → ref#123 (copy reference)
obj2.value = 20;          // Heap: ref#123 → { value: 20 }
console.log(obj1.value);  // 20! (cùng object)

Vòng đời bộ nhớ

  1. Allocate (Cấp phát): Khai báo biến, tạo object, gọi hàm.
  2. Use (Sử dụng): Đọc/ghi biến, property, element.
  3. Release (Giải phóng): Ra khỏi scope (stack), GC dọn heap (unreachable).

Garbage Collection (GC) trong JS

  • Mark-and-Sweep (Đánh dấu & Quét): Từ root (global/window), đánh dấu tất cả object reachable. Object không đánh dấu → sweep (dọn).
  • Generational GC: Tách young generation (mới, chết nhanh) và old generation (lâu sống) – tối ưu hiệu năng (V8 Orinoco).
  • Incremental/Concurrent GC: Chạy song song với JS thread, không block lâu.

Memory Leak (Rò rỉ bộ nhớ) thường gặp


// 1. Global variable vô tình
function leak() { accidentalGlobal = "leak"; } // window.accidentalGlobal

// 2. Closure giữ reference không cần
function createLeak() {
  const bigData = new Array(1000000).fill("x");
  return function() { return bigData.length; }; // bigData không bao giờ GC
}

// 3. Event listener không remove
element.addEventListener('click', handler);
// element removed from DOM but handler still references it

// 4. Timer không clear
setInterval(() => { ... }, 1000); // Quên clearInterval

// 5. Cache không giới hạn
const cache = {};
function get(key) {
  if (!cache[key]) cache[key] = expensiveCompute(key);
  return cache[key]; // Cache tăng vô hạn
}

Best Practice

  • Dùng const/let block scope – biến sớm hết scope → sớm GC.
  • Nullify reference khi không dùng: bigObj = null.
  • Remove event listener: element.removeEventListener('click', handler).
  • Clear timer: clearInterval(id), clearTimeout(id).
  • Dùng WeakMap/WeakSet cho cache/metadata – không ngăn GC key.
  • DevTools Memory tab: Heap snapshot, Allocation timeline để profile leak.

Tóm tắt

  • Primitive → Stack (giá trị). Object → Stack (reference) + Heap (object).
  • Stack: tự động cấp phát/giải phóng theo scope (LIFO).
  • Heap: GC tự động dọn object unreachable (Mark-and-Sweep).
  • Memory leak = reference vô tình giữ object không dùng nữa.