🌐 Detecting your location…
📢 Advertisement — Configure AdSense in Appearance → Customize → AdSense Settings

كيفية تصحيح أخطاء تسرب الذاكرة في Node.js: الدليل الكامل خطوة بخطوة 2026

⏱️3 min read  ·  632 words

{
“@context”: “https://schema.org”,
“@type”: “TechArticle”,
“headline”: “كيفية تصحيح أخطاء تسرب الذاكرة في Node.js: الدليل الكامل خطوة بخطوة 2026”,
“description”: “يمكنك تشخيص وإصلاح تسرب ذاكرة Node.js باستخدام Chrome DevTools وclinic.js وعلامة –inspect. يغطي أنماط التسرب الشائعة مع أمثلة التعليمات البرمجية الحقيقية.”,
“url”: “https://techpulsesite.com/how-to-debug-memory-leaks-in-node-js-com-ar/”,
“datePublished”: “2026-06-28T16:50:00+00:00”,
“dateModified”: “2026-06-29T04:14:13+00:00”,
“author”: {
“@type”: “Organization”,
“name”: “TechPulse Editorial Team”,
“url”: “https://techpulsesite.com”
},
“publisher”: {
“@type”: “Organization”,
“name”: “TechPulse”,
“url”: “https://techpulsesite.com”
},
“inLanguage”: “ar”
}

{
“@context”: “https://schema.org”,
“@type”: “TechArticle”,
“headline”: “كيفية تصحيح أخطاء تسرب الذاكرة في Node.js: الدليل الكامل خطوة بخطوة 2026”,
“description”: “يمكنك تشخيص وإصلاح تسرب ذاكرة Node.js باستخدام Chrome DevTools وclinic.js وعلامة –inspect. يغطي أنماط التسرب الشائعة مع أمثلة التعليمات البرمجية الحقيقية.”,
“url”: “https://techpulsesite.com/how-to-debug-memory-leaks-in-node-js-com-ar/”,
“datePublished”: “2026-06-28T16:50:00+00:00”,
“dateModified”: “2026-06-28T18:23:33+00:00”,
“author”: {
“@type”: “Organization”,
“name”: “TechPulse Editorial Team”,
“url”: “https://techpulsesite.com”
},
“publisher”: {
“@type”: “Organization”,
“name”: “TechPulse”,
“url”: “https://techpulsesite.com”
},
“inLanguage”: “ar”
}

نود.جي إستسرب الذاكرة يتسبب في أن العملية تستهلك المزيد من ذاكرة الوصول العشوائي تدريجيًا حتى تتعطل معFATAL ERROR: Reached heap limit أو يصبح بطيئًا جدًا في تلبية الطلبات. في الإنتاج، يظهر هذا غالبًا عند إعادة تشغيل الخادم أسبوعيًا أو انخفاض أوقات الاستجابة. وإليك كيفية العثور عليها وإصلاحها.

ما هو تسرب الذاكرة في Node.js؟

يحدث تسرب للذاكرة عندما يتم تخصيص كائنات في الذاكرة ولكن لا يتم تحريرها مطلقًا – لأن شيئًا ما لا يزال يحمل مرجعًا إليها، مما يمنع تجميع البيانات المهملة. الأسباب الشائعة: المتغيرات العامة التي تتراكم البيانات، وعدم إزالة مستمعي الأحداث مطلقًا، وعمليات الإغلاق التي تلتقط كائنات كبيرة، وذاكرة التخزين المؤقت التي تنمو بشكل غير محدود، وعمليات رد الاتصال المؤقتة التي تحتوي على مراجع للكائنات.

الخطوة 1: تأكد من وجود تسرب للذاكرة

// Monitor memory usage in your application
function logMemory() {
  const used = process.memoryUsage();
  console.log({
    heapUsed:   `${Math.round(used.heapUsed / 1024 / 1024)}MB`,
    heapTotal:  `${Math.round(used.heapTotal / 1024 / 1024)}MB`,
    rss:        `${Math.round(used.rss / 1024 / 1024)}MB`,
    external:   `${Math.round(used.external / 1024 / 1024)}MB`,
  });
}

setInterval(logMemory, 5000);  // log every 5 seconds under load

قم بإجراء اختبارات الحمل وشاهد الأرقام. إذاheapUsed ينمو بشكل مستمر ولا ينخفض أبدًا بعد دورات GC، فهذا يعني أن لديك تسربًا.

الخطوة 2: استخدم –inspect + Chrome DevTools

# Start Node with inspector
node --inspect app.js
# or for existing process
kill -USR1 <PID>

افتح Chrome وانتقل إلىchrome://inspect. انقر على “فتح أدوات DevTools المخصصة للعقدة”. في علامة التبويب “الذاكرة”، التقط لقطة كومة، وقم بإنشاء التحميل، وانتظر 60 ثانية، ثم التقط لقطة أخرى. قارنهم:

في اللقطة الثانية، حدد “مقارنة” من القائمة المنسدلة. قم بالفرز حسب “حجم دلتا” – الكائنات التي تنمو بشكل أسرع هي العناصر المرشحة للتسرب.

الخطوة 3: استخدمclinic.js للتشخيص التلقائي

npm install -g clinic

# Run your app under clinic
clinic doctor -- node app.js

# More detailed heap analysis
clinic heapprofiler -- node app.js

# Flame graph for CPU + memory
clinic flame -- node app.js

تقوم Clinic بإنشاء تقرير HTML يوضح نمو الذاكرة بمرور الوقت، وتحدد الفواصل الزمنية التي بها مشكلات، وغالبًا ما تشير مباشرة إلى نمط التعليمات البرمجية المتسرب.

نمط التسرب الشائع 1: لم تتم إزالة مستمعي الأحداث

// 🐛 Bug: listener added but never removed
class DataProcessor {
  constructor(emitter) {
    emitter.on('data', this.processData.bind(this));
    // When DataProcessor is destroyed, emitter still holds reference
    // → DataProcessor and its data are never GC'd
  }
}

// ✅ Fix: remove listener when done
class DataProcessor {
  constructor(emitter) {
    this.emitter = emitter;
    this.handler = this.processData.bind(this);
    emitter.on('data', this.handler);
  }

  destroy() {
    this.emitter.off('data', this.handler);  // cleanup
  }
}

// Check for listener leaks (Node warns at 11+ listeners)
emitter.setMaxListeners(20);  // raise limit if legitimately needed
console.log(emitter.listenerCount('data'));  // monitor count

نمط التسرب الشائع 2: ذاكرة تخزين مؤقت غير محدودة أو خريطة

// 🐛 Bug: cache grows forever
const cache = new Map();
function getCachedData(key) {
  if (!cache.has(key)) {
    cache.set(key, fetchFromDB(key));  // never evicted
  }
  return cache.get(key);
}

// ✅ Fix: bounded LRU cache
const LRU = require('lru-cache');
const cache = new LRU({ max: 1000, ttl: 1000 * 60 * 5 });  // 5-min TTL, max 1000 entries

// Or simple manual TTL approach
const cache = new Map();
function getCachedData(key) {
  const entry = cache.get(key);
  if (entry && Date.now() - entry.time < 300000) {
    return entry.data;
  }
  const data = fetchFromDB(key);
  cache.set(key, { data, time: Date.now() });
  // Evict old entries periodically
  if (cache.size > 1000) cache.delete(cache.keys().next().value);
  return data;
}

نمط التسرب الشائع 3: عمليات الإغلاق التي تلتقط الأجسام الكبيرة

// 🐛 Bug: closure keeps large data alive
function processLargeFile(data) {
  const hugeBuffer = Buffer.alloc(100 * 1024 * 1024); // 100MB
  hugeBuffer.copy(data);

  return function processChunk(chunk) {
    // This closure captures hugeBuffer — stays in memory as long as
    // processChunk function exists, even if we only need one small field
    return hugeBuffer.slice(0, 10);
  };
}

// ✅ Fix: extract only what you need before creating closure
function processLargeFile(data) {
  const hugeBuffer = Buffer.alloc(100 * 1024 * 1024);
  hugeBuffer.copy(data);
  const needed = hugeBuffer.slice(0, 10);  // extract needed data
  // hugeBuffer can now be GC'd — closure only captures 'needed'
  return function processChunk(chunk) {
    return needed;
  };
}

نموذج التسرب الشائع 4: setInterval بدون ClearInterval

// 🐛 Bug: interval keeps closure alive forever
function startWorker(data) {
  const interval = setInterval(() => {
    process(data);  // data never released because interval holds it
  }, 1000);
  // interval never cleared when worker is "stopped"
}

// ✅ Fix: always clear intervals
function startWorker(data) {
  const interval = setInterval(() => {
    process(data);
  }, 1000);

  return function stop() {
    clearInterval(interval);  // release the closure reference
  };
}

الخطوة 4: تحليل تفريغ الكومة باستخدام heapdump

npm install heapdump

// In your app — trigger a dump on SIGUSR2
const heapdump = require('heapdump');
process.on('SIGUSR2', () => {
  heapdump.writeSnapshot((err, filename) => {
    console.log('Heap snapshot written to', filename);
  });
});

// Trigger from terminal
kill -USR2 <PID>

افتح ملف .heapsnapshot في Chrome DevTools (علامة تبويب الذاكرة → تحميل → تحديد ملف). قم بتصفية الكائنات التي تشك فيها. ابحث عن الكائنات ذات “الحجم المحتجز” العالي – وهي الذاكرة التي يحتفظ بها هذا الكائن.

الأسئلة المتداولة

س: ما الفرق بين الكومة المستخدمة و RSS؟
A: heapUsed هي ذاكرة كومة الذاكرة المؤقتة V8 JavaScript. RSS (حجم المجموعة المقيم) هو إجمالي ذاكرة العملية بما في ذلك المخازن المؤقتة الأصلية والتعليمات البرمجية والكومة. تشير خدمة RSS المتزايدة دون زيادة الكومة المستخدمة إلى حدوث تسرب للوحدة الأصلية.

س: هل يمكنني فرض اختبار جمع البيانات المهملة؟
ج: نعم:node --expose-gc app.js ثم اتصلglobal.gc(). إذا انخفضت الذاكرة بشكل ملحوظ بعد GC اليدوية ولكنها تنمو مرة أخرى، فهذا يعني أنك أكدت حدوث تسرب في الإنتاج.

س: كيف يمكنني اكتشاف التسريبات في الإنتاج؟
ج: استخدم أدوات APM: تقوم كل من Datadog أو New Relic أو Elastic APM بمراقبة نمو الكومة بمرور الوقت. ضبط التنبيهات عند استخدام الكومة بنسبة 80%. ضع في اعتبارك أيضًا--max-old-space-size=512 للحد من الكومة والفشل بسرعة بدلاً من التدهور البطيء.

س: هل WeakMap/WeakRef مفيد لمنع التسربات؟
ج: نعم. WeakMap وWeakSet الاحتفاظ بمراجع ضعيفة – إذا لم يكن للكائن الرئيسي أي مراجع أخرى، فهو مؤهل للحصول على GC حتى لو كان مفتاحًا في WeakMap. مفيد للتخزين المؤقت لبيانات التعريف الخاصة بالكائنات دون منع جمعها.

س: ما هو الحد الأقصى لحجم الكومة المناسب لـ Node.js؟
ج: الافتراضي هو ~1.5 جيجابايت (64 بت). اضبط بشكل صريح مع--max-old-space-size=2048 (2 جيجابايت). الحجم يصل إلى 70-80% من ذاكرة الوصول العشوائي المتوفرة، مما يترك مساحة لنظام التشغيل والعمليات الأخرى.

الخلاصة

يمكن تشخيص حالات تسرب ذاكرة Node.js وإصلاحها باستخدام الأدوات المناسبة. سير العمل:تأكيد التسرب من خلال تسجيل الذاكرة ← العزل باستخدام لقطات كومة أدوات Chrome DevTools ← تحديد النمط (المستمع، ذاكرة التخزين المؤقت، الإغلاق، المؤقت) ← الإصلاح والتحقق. تمثل الأنماط الأربعة الواردة في هذا الدليل 90% من حالات تسرب ذاكرة الإنتاج. عندما تكون في شك، سوف توجهكclinic.js في الاتجاه الصحيح خلال دقائق.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা