Node Chrome-এর মতো একই V8 JavaScript engine ব্যবহার করে, আর আগের JavaScript কোর্সের Memory Management lesson থেকে একই automatic memory management — কিছু আর reference না করলে memory free হয়। একটা long-running server একটা browser tab-এর চেয়ে একটা leak-এর পরিণতি অনেক বেশি দৃশ্যমান বানায়।
একটা browser tab বন্ধ হয় আর এর memory প্রায়ই মিনিটের মধ্যে সম্পূর্ণভাবে reclaim হয়। একটা Node server দিন বা সপ্তাহ ধরে চলার জন্য বানানো — একটা পাঁচ-মিনিটের test-এ অদৃশ্য একটা ধীর leak যথেষ্ট traffic-এর পর memory শেষ হয়ে যাওয়া একটা crash হয়ে যায়।
// LEAK — এই Map কখনো কিছু সরায় না, চিরকাল বাড়তে থাকে
const cache = new Map()
app.get('/users/:id', async (req, res) => {
if (!cache.has(req.params.id)) {
cache.set(req.params.id, await getUser(req.params.id))
}
res.json(cache.get(req.params.id))
})// FIXED — একটা আসল cache-এর একটা eviction strategy দরকার
import { LRUCache } from 'lru-cache'
const cache = new LRUCache({ max: 500 }) // সর্বোচ্চ ৫০০টা entry রাখে// LEAK — প্রতিটা request-এ একটা নতুন listener যোগ হয়, কোনোটাই সরানো হয় না
app.get('/subscribe', (req, res) => {
eventEmitter.on('update', (data) => {
res.write(data)
})
})একটা built-in আগাম সতর্কতা
Default-এ একই event-এ ১০টা listener জমা হওয়ার পর Node একটা warning print করে — console-এ একটা "MaxListenersExceededWarning" একটা শক্তিশালী ইঙ্গিত যে কোথাও একটা listener যোগ হচ্ছে যা কখনো সরানো হচ্ছে না।
console.log(process.memoryUsage())
// { rss: ..., heapTotal: ..., heapUsed: ..., external: ... }
// স্থির load-এ সময়ের সাথে ক্রমাগত বাড়তে থাকা heapUsed একটা leak-এর signaturenode --inspect server.js
# তারপর DevTools attach করতে Chrome-এ chrome://inspect খুলুন —
# Memory tab একটা heap snapshot নিতে পারে, আগের JavaScript lesson-এ
# browser-side leak খোঁজার জন্য ব্যবহৃত একই tool| Common source | সমাধান |
|---|---|
| একটা unbounded cache (শুধু বাড়তে থাকা একটা plain object বা Map) | একটা size limit আর eviction (LRU) সহ একটা আসল cache ব্যবহার করুন |
| বারবার যোগ করা event listener, কখনো সরানো হয় না | শেষ হলে .removeListener() / .off() call করুন, বা one-time listener-এর জন্য .once() ব্যবহার করুন |
| একটা module-level array বা object যা জমা হতে থাকে | স্পষ্টভাবে এটা clear বা bound করুন, বা module-এর বদলে একটা request-এ scope করুন |
| একটা ভুলে যাওয়া setInterval | Interval ID সংরক্ষণ করুন আর আর দরকার না হলে clearInterval() করুন |