Bước 1 — xác nhận đó là rò rỉ heap. Ghi lại process.memoryUsage() theo chu kỳ. Nếu heapUsed tăng đều và không tụt sau GC thì là rò rỉ JS object. Nếu heapUsed phẳng mà rss vẫn tăng thì nghi Buffer/native addon (heap snapshot sẽ không chỉ ra).
Bước 2 — lấy snapshot đúng thời điểm. Cần ba snapshot: sau khi khởi động và ổn định, sau một đợt tải, và sau đợt tải thứ hai. Cách lấy trên production:
# start with a signal handler for on-demand snapshots
node --heapsnapshot-signal=SIGUSR2 server.js
kill -USR2 <pid>Hoặc gọi v8.writeHeapSnapshot() từ một endpoint nội bộ có bảo vệ. Lưu ý snapshot chặn event loop và file có thể vài trăm MB — làm trên một instance đã rút khỏi load balancer.
Bước 3 — so sánh. Mở Chrome DevTools → Memory, nạp snapshot cũ trước rồi snapshot mới, chọn chế độ Comparison. Sắp xếp theo Delta để tìm loại object tăng số lượng qua cả hai đợt tải — object chỉ tăng một đợt thường là cache đang ấm lên, không phải rò rỉ.
Bước 4 — truy retainer. Chọn object nghi ngờ, xem Retainers để lần ngược chuỗi tham chiếu tới gốc. Các nguyên nhân lặp lại nhiều nhất: Map/Set cấp module dùng làm cache không có giới hạn hay TTL, listener đăng ký mỗi request, closure bị giữ bởi setInterval không clearInterval, và mảng log/metric tích lũy trong bộ nhớ.
Bước 5 — sửa và kiểm chứng bằng cặp snapshot mới trên cùng kịch bản tải. Nếu nghi native, đổi hướng sang --max-old-space-size để phân biệt và dùng công cụ theo dõi RSS ngoài heap.