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 đi ngang 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:
# chạy kèm signal handler để lấy snapshot khi cần
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 warm up, 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 do setInterval giữ lại mà không clearInterval, và mảng log/metric tích luỹ 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.