حل مشاكل الذاكرة

تعرَّف على كيفية استخدام Chrome وDevTools لتحديد مشاكل الذاكرة التي تؤثر في أداء الصفحة، بما في ذلك تسرُّب الذاكرة ومعدّل انتفاخ الذاكرة وظهور البيانات المهملة بشكل متكرر.

ملخّص

  • يمكنك التعرف على حجم الذاكرة التي تستخدمها صفحتك حاليًا من خلال ميزة "إدارة المهام" في Chrome.
  • يمكنك عرض استخدام الذاكرة بمرور الوقت من خلال تسجيلات "المخطّط الزمني".
  • يمكنك تحديد أشجار DOM المنفصلة (أحد الأسباب الشائعة لتسرّب الذاكرة) باستخدام لقطات Heap Snapshots.
  • يمكنك معرفة وقت تخصيص ذكرى جديدة في كومة JavaScript من خلال تسجيلات المخطط الزمني للتخصيص.

نظرة عامة

تماشيًا مع نموذج أداء RAIL، يجب أن يكون تركيز جهود الأداء على المستخدمين.

مشكلات الذاكرة مهمة لأن المستخدمين غالبًا ما يلاحظونها. يمكن للمستخدمين إدراك مشكلات الذاكرة بالطرق التالية:

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

تعدّد الذاكرة: ما مقدار "زيادة كبيرة جدًا"؟

من السهل تحديد تسرُّب الذاكرة. إذا كان أحد المواقع يستخدم ذاكرة متزايدة تدريجيًا، هذا يعني أنّ هناك تسريب ما. ولكن من الصعب تحديد زيادة حجم الذاكرة. ما الذي يعتبر "استخدام مساحة كبيرة جدًا من الذاكرة"؟

ما مِن أرقام ثابتة هنا، لأنّ الإمكانيات اللازمة للأجهزة والمتصفحات المختلفة تختلف. قد تتعطل الصفحة نفسها التي تعمل بسلاسة على هاتف ذكي متطور على هاتف ذكي منخفض المواصفات.

المفتاح هنا هو استخدام نموذج RAIL والتركيز على المستخدمين. تعرّف على الأجهزة الرائجة لدى المستخدمين، ثم اختبِر صفحتك على تلك الأجهزة. إذا كانت التجربة سيئة باستمرار، قد يعني ذلك أنّ الصفحة تتجاوز إمكانات الذاكرة لهذه الأجهزة.

مراقبة استخدام الذاكرة في الوقت الفعلي من خلال ميزة "إدارة المهام" في Chrome

ويمكنك استخدام ميزة "إدارة مهام Chrome" كنقطة بداية للتحقيق في مشاكل الذاكرة. مدير المهام هو أداة مراقبة في الوقت الفعلي تخبرك بحجم الذاكرة التي تستخدمها الصفحة حاليًا.

  1. اضغط على Shift+Esc أو انتقل إلى قائمة Chrome الرئيسية واختر المزيد من الأدوات > إدارة المهام لفتح "إدارة المهام".

    فتح إدارة المهام

  2. انقر بزر الماوس الأيمن على عنوان الجدول في "إدارة المهام" وفعِّل ذاكرة JavaScript.

    جارٍ تفعيل ذاكرة JavaScript

يوضح لك هذان العمودان أشياء مختلفة حول كيفية استخدام صفحتك للذاكرة:

  • يمثل عمود الذاكرة الذاكرة الأصلية. يتم تخزين عُقد DOM في الذاكرة الأصلية. إذا كانت هذه القيمة متزايدة، يتم إنشاء عُقد DOM.
  • يمثّل عمود ذاكرة JavaScript كومة JavaScript. يحتوي هذا العمود على قيمتين. القيمة التي تهتم بها هي الرقم المباشر (الرقم بين قوسين). يمثل الرقم المباشر مقدار الذاكرة التي تستخدمها العناصر التي يمكن الوصول إليها على صفحتك. إذا كان هذا العدد يزداد، فإما أنه يتم إنشاء كائنات جديدة، أو تزايد الكائنات الموجودة.

عرض عمليات تسرُّب الذاكرة باستخدام تسجيلات الأداء

ويمكنك أيضًا استخدام لوحة "الأداء" كنقطة بداية أخرى في التحقيق. تساعدك لوحة "الأداء" في عرض استخدام ذاكرة الصفحة بمرور الوقت.

  1. افتح لوحة الأداء في "أدوات مطوري البرامج".
  2. فعِّل مربّع الاختيار الذاكرة.
  3. إنشاء تسجيل:

لعرض تسجيلات الأداء الخاصة بالذاكرة، يمكنك استخدام الرمز أدناه:

var x = [];

function grow() {
  for (var i = 0; i < 10000; i++) {
    document.body.appendChild(document.createElement('div'));
  }
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

في كل مرة يتم فيها الضغط على الزر المُشار إليه في الرمز، يتم إلحاق عشرة آلاف عقدة div بنص المستند، ويتم دفع سلسلة مكوّنة من مليون حرف x إلى مصفوفة x. يؤدي تشغيل هذا الرمز إلى إنشاء تسجيل لمخطط زمني مثل لقطة الشاشة التالية:

مثال بسيط للنمو

أولهما، شرح لواجهة المستخدم يمثل الرسم البياني HEAP في لوحة نظرة عامة (أسفل NET) كومة JavaScript. أسفل لوحة نظرة عامة، يوجد جزء العدّاد. في هذه الصفحة، يمكنك الاطّلاع على بيانات استخدام الذاكرة مقسّمة حسب كومة JavaScript (مماثلة للرسم البياني HEAP في لوحة النظرة العامة) والمستندات وعُقد DOM والمستمعين وذاكرة وحدة معالجة الرسومات. ويؤدّي إيقاف مربّع اختيار إلى إخفائها من الرسم البياني.

الآن، تحليل التعليمة البرمجية مقارنة بلقطة الشاشة. إذا نظرت إلى عدّاد العُقد (الرسم البياني الأخضر)، يمكنك ملاحظة أنّه يتطابق تمامًا مع الرمز. يزداد عدد العقد بخطوات منفصلة. يمكنك أن تفترض أن كل زيادة في عدد العُقد هي استدعاء لـ grow(). إن الرسم البياني لكومة JavaScript (الرسم البياني الأزرق) ليس سهلاً. وتماشيًا مع أفضل الممارسات، يكون الانخفاض الأول في الواقع عملية جمع البيانات غير المرغوب فيها (يتم تحقيقه بالضغط على زر جمع البيانات غير المرغوب فيها). ومع تقدّم التسجيل، يمكنك أن تلاحظ ازدياد حجم كومة الذاكرة المؤقتة في JavaScript. هذا أمر طبيعي ومتوقع: ينشئ رمز JavaScript عُقد DOM عند كل نقرة على الزر ويقوم بالكثير من العمل عندما ينشئ سلسلة مكوّنة من مليون حرف. الشيء الرئيسي هنا هو حقيقة أن كومة JavaScript تنتهي أعلى مما بدأت ("البداية" هنا هي النقطة التي تلي جمع البيانات غير الضرورية المفروضة). إذا رأيت في العالم الواقعي هذا النمط من زيادة حجم كومة الذاكرة المؤقتة أو حجم العُقدة في JavaScript، قد يعني ذلك حدوث تسرّب للذاكرة.

اكتشاف عمليات تسرّب الذاكرة التي تم فصلها عن شجرة نموذج العناصر في المستند (DOM) باستخدام ميزة Heap Snapshots

لا يمكن أن تكون عقدة DOM سوى محتوى غير صحيح لا يمكن جمعه إلا في حال عدم توفُّر مراجع من شجرة DOM للصفحة أو رمز JavaScript. يُقال إنّ العقدة "مفصولة" عند إزالتها من شجرة نموذج العناصر في المستند (DOM) ولكن لا تزال بعض رموز JavaScript تشير إليها. تعتبر عُقد DOM المنفصلة سببًا شائعًا لتسرّب الذاكرة. يعلمك هذا القسم كيفية استخدام أدوات تحليل كومة الذاكرة المؤقتة في DevTools لتحديد العُقد المنفصلة.

إليك مثال بسيط على عُقد DOM المنفصلة.

var detachedTree;

function create() {
  var ul = document.createElement('ul');
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li');
    ul.appendChild(li);
  }
  detachedTree = ul;
}

document.getElementById('create').addEventListener('click', create);

يؤدي النقر على الزر المشار إليه في الرمز إلى إنشاء عقدة ul تحتوي على عشرة عناصر li ثانوية. تتم الإشارة إلى هذه العُقد بواسطة الرمز إلا أنها غير موجودة في شجرة نموذج العناصر في المستند (DOM)، لذا يتم فصلها.

لقطات لقطات لعناصر متعددة هي طريقة لتحديد العُقد المنفصلة. وكما يوحي الاسم، تُظهر لك اللقطات المكدسة كيفية توزيع الذاكرة بين كائنات JS لصفحتك وعُقد DOM في وقت اللقطة.

لإنشاء لقطة، افتح "أدوات مطوري البرامج" وانتقِل إلى لوحة الذاكرة، وانقر على زر الاختيار لقطة مجمّعة، ثم اضغط على الزر أخذ لقطة.

أخذ لقطة متعدّدة

قد تستغرق معالجة اللقطة وتحميلها بعض الوقت. بعد الانتهاء من ذلك، اختره من اللوحة اليمنى (التي تحمل اسم HEAP SNAPSHOTS).

اكتب Detached في مربّع النص فلتر الفئة للبحث عن أشجار DOM المنفصلة.

فلترة العُقد المنفصلة

قم بتوسيع القيراط لفحص شجرة منفصلة.

البحث في شجرة منفصلة

تشتمل العُقد المميزة باللون الأصفر على إشارات مباشرة إليها من رمز JavaScript. لا تحتوي العُقد المميزة باللون الأحمر على مراجع مباشرة. وهي تكون على قيد الحياة فقط لأنها جزء من شجرة العقدة الصفراء. بشكل عام، تريد التركيز على العقد الصفراء. أصلح التعليمة البرمجية بحيث لا تبقى العقدة الصفراء نشطة لفترة أطول مما ينبغي، وتتخلص أيضًا من العقد الحمراء التي تعتبر جزءًا من شجرة العقدة الصفراء.

انقر على عقدة صفراء لمزيد من التحقق منها. في جزء Objects (العناصر)، يمكنك الاطّلاع على مزيد من المعلومات حول الرمز الذي يشير إليه. على سبيل المثال، في لقطة الشاشة أدناه، يمكنك أن ترى أن المتغير detachedTree يشير إلى العقدة. لإصلاح هذا التسرّب المحدد للذاكرة، يجب دراسة الرمز الذي يستخدم detachedTree والتأكد من أنه يزيل مرجعه إلى العُقدة عندما لا تكون هناك حاجة إليها.

التحقّق من نقطة صفراء

تحديد حالات تسرّب ذاكرة JavaScript باستخدام "المخططات الزمنية للتخصيص"

"المخطط الزمني للتخصيص" هو أداة أخرى يمكنها مساعدتك في تتبُّع تسرُّب الذاكرة في كومة الذاكرة المؤقتة في JavaScript.

لتوضيح المخطط الزمني للتخصيص، يمكنك استخدام الرمز التالي:

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

وفي كل مرة يتم فيها الضغط على الزر المشار إليه في الرمز، تتم إضافة سلسلة مكوّنة من مليون حرف إلى مصفوفة x.

لتسجيل مخطّط زمني للتخصيص، افتح أدوات مطوّري البرامج وانتقِل إلى لوحة الملفات الشخصية وانقر على زر الاختيار تسجيل المخطط الزمني للتخصيص واضغط على الزر بدء ونفّذ الإجراء الذي تشكّ في أنّه يتسبب في تسرّب الذاكرة، ثم اضغط على الزر إيقاف التسجيل (زر إيقاف التسجيل) عند الانتهاء.

أثناء التسجيل، يُرجى ملاحظة ما إذا كانت هناك أشرطة زرقاء ظاهرة في المخطط الزمني للتخصيص، كما هو الحال في لقطة الشاشة أدناه.

عمليات توزيع جديدة

تمثل تلك الأشرطة الزرقاء عمليات تخصيص جديدة للذاكرة. وتكون عمليات تخصيص الذاكرة الجديدة هذه مرشحة لتسرُّب الذاكرة. يمكنك تكبير/تصغير أي شريط لفلترة لوحة المنشئ لعرض العناصر التي تم تخصيصها خلال الإطار الزمني المحدّد فقط.

المخطط الزمني لعملية توزيع التكبير أو التصغير

وسِّع الكائن وانقر على قيمته لعرض المزيد من التفاصيل عنه في لوحة الكائن. على سبيل المثال، في لقطة الشاشة أدناه، من خلال عرض تفاصيل العنصر الذي تم تخصيصه حديثًا، سيكون بإمكانك معرفة أنّه قد تم تخصيصه للمتغيّر x في نطاق Window.

تفاصيل العنصر

تحديد تخصيص الذاكرة حسب الدالة

استخدِم نوع أخذ العينات في لوحة الذاكرة لعرض تخصيص الذاكرة حسب دالة JavaScript.

محلّل تخصيص السجلات

  1. حدّد زر الاختيار أخذ العينات لتخصيص المحتوى. إذا كان هناك عامل على الصفحة، يمكنك اختياره كهدف إنشاء ملفات التعريف باستخدام القائمة المنسدلة بجانب الزر Start (بدء).
  2. اضغط على زر البدء.
  3. نفِّذ الإجراءات على الصفحة التي تريد التحقيق فيها.
  4. اضغط على الزر إيقاف عند الانتهاء من جميع الإجراءات.

تعرض لك "أدوات مطوري البرامج" تفاصيل عن تخصيص الذاكرة حسب الوظيفة. طريقة العرض التلقائية هي ثقيل (من أسفل لأعلى)، حيث تعرض الدوال التي خصصت أكبر قدر من الذاكرة في الجزء العلوي.

الملف الشخصي للتخصيص

رصد عمليات تجميع النفايات المتكررة

إذا تم إيقاف صفحتك مؤقتًا بشكل متكرر، قد يعني ذلك أنّك تواجه مشاكل في جمع البيانات غير المرغوب فيها.

يمكنك استخدام "إدارة المهام" في Chrome أو تسجيلات الذاكرة ضمن "المخطط الزمني" لرصد عمليات تجميع البيانات المهملة بشكل متكرر. في إدارة المهام، تمثل قيم الذاكرة أو ذاكرة JavaScript الهابطة أو الصاعدة بشكل متكرر مجموعات بيانات غير مهمة بشكل متكرر. في تسجيلات المخطط الزمني، تشير الرسوم البيانية التي ترتفع أو تنخفض لعدد مضاعفات JavaScript أو أعداد العُقد بشكلٍ متكرر إلى مجموعات بيانات غير مهمة بشكل متكرر.

بعد تحديد المشكلة، يمكنك استخدام تسجيل "المخطط الزمني للتخصيص" لمعرفة مكان تخصيص الذاكرة والوظائف التي تؤدي إلى عمليات التخصيص.