يصف هذا القسم المصطلحات الشائعة المستخدمة في تحليل الذاكرة، وهو ينطبق على مجموعة متنوعة من أدوات تحليل الذاكرة للغات المختلفة.
تشير المصطلحات والمفاهيم الموضّحة هنا إلى محلِّل Chrome DevTools Heap. وإذا سبق لك أن عملت مع Java، أو .NET، أو أي برنامج تحليل آخر للذاكرة، فقد يكون هذا تنشيطًا للذاكرة.
أحجام العناصر
فكر في الذاكرة كرسم بياني به أنواع أساسية (مثل الأرقام والسلاسل) والكائنات (المصفوفات الرابطية). قد يتم تمثيلها بصريًا كرسم بياني مع عدد من النقاط المترابطة على النحو التالي:
يمكن أن يحتفظ الكائن بالذاكرة بطريقتين:
- مباشرةً من العنصر نفسه
- ويكون ذلك ضمنًا عن طريق الاحتفاظ بمراجع لكائنات أخرى، وبالتالي منع التخلص من هذه الأشياء تلقائيًا بواسطة جامع القمامة (GC اختصارًا).
عند العمل مع أداة تحليل عناصر Heap في "أدوات مطوري البرامج" (وهي أداة للتحقيق في مشاكل الذاكرة التي يتم العثور عليها ضمن "الملفات الشخصية")، من المحتمل أن تجد نفسك تطّلع على بضعة أعمدة مختلفة من المعلومات. هناك قطعتان بارزتان هما الحجم الضحل والحجم المحتفظ به، ولكن ماذا يمثّلان؟
حجم مناسب
هذا هو حجم الذاكرة التي يحتفظ بها الكائن نفسه.
تتضمَّن كائنات JavaScript النموذجية بعض الذاكرة المخصّصة لوصفها ولتخزين القيم الفورية. عادةً ما يكون للصفائف والسلاسل حجم سطحي كبير. ومع ذلك، غالبًا ما يكون للسلاسل والصفائف الخارجية مساحة التخزين الرئيسية في ذاكرة العارض، ما يعرض كائن برنامج تضمين صغير فقط في كومة JavaScript البرمجية.
ذاكرة العارض هي ذاكرة كاملة للعملية التي يتم فيها عرض الصفحة التي تم فحصها: الذاكرة الأصلية + ذاكرة JavaScript للذاكرة الخاصة بالصفحة + ذاكرة كومة JavaScript لجميع العاملين الذين بدأوا العملية من خلال الصفحة. وبرغم ذلك، يمكن أن يحتفظ كائن صغير بكمية كبيرة من الذاكرة بطريقة غير مباشرة، وذلك عن طريق منع التخلص من الكائنات الأخرى من خلال عملية جمع البيانات غير المرغوب فيها تلقائيًا.
الحجم المحتفظ به
هذا هو حجم الذاكرة التي يتم تحريرها بمجرد حذف الكائن نفسه مع الكائنات التابعة له التي تعذَّر الوصول إليها من جذور GC.
تتكوّن جذور GC من الأسماء المعرِّفة التي يتم إنشاؤها (إما محلية أو عالمية) عند إنشاء مرجع من رمز أصلي إلى كائن JavaScript خارج V8. يمكن العثور على جميع هذه الأسماء المعرِّفة في لقطة لأجزاء من الذاكرة ضمن جذور GC > نطاق الاسم المعرِّف وجذور GC > الأسماء المعرِّفة العامة. قد يكون وصف الأسماء المعرِّفة في هذه المستندات بدون الغوص في تفاصيل طريقة استخدام المتصفِّح أمرًا مربكًا. لذا، لا داعي للقلق بشأن جذور GC والأزرار.
هناك الكثير من جذور تجميع البيانات المهملة الداخلية التي لا تهم المستخدمين. من وجهة نظر التطبيقات، هناك الأنواع التالية من الجذور:
- كائن عام للنافذة (في كل إطار iframe). يتوفّر حقل مسافة في اللقطات لأجزاء من الذاكرة، وهو عدد مراجع الخصائص في أقصر مسار احتفاظ من النافذة.
- شجرة نموذج كائن المستند (DOM) التي تتألف من جميع عُقد DOM الأصلية التي يمكن الوصول إليها باجتياز المستند قد لا يكون لدى جميع أغلفة JavaScript ولكن إذا كانت عليها نشطة، فلن يبقى فيها المستند صالحًا.
- في بعض الأحيان، قد يتم الاحتفاظ بالكائنات من خلال سياق برنامج تصحيح الأخطاء ووحدة تحكّم أدوات مطوّري البرامج (على سبيل المثال، بعد تقييم وحدة التحكّم). يمكنك إنشاء لقطات لأجزاء من الذاكرة باستخدام وحدة تحكّم واضحة وبدون نقاط توقف نشطة في برنامج تصحيح الأخطاء.
يبدأ الرسم البياني للذاكرة بجذر، وقد يكون الكائن window
في المتصفّح أو الكائن Global
في وحدة Node.js. ولا يمكنك التحكّم في طريقة تجميع البيانات التي تم الحصول عليها من الكائن الجذر (GC) هذا.
وأي شيء لا يمكن الوصول إليه من الجذر يحصل على تجميع البيانات المهملة.
عناصر الاحتفاظ بالشجرة
كومة الذاكرة المؤقتة عبارة عن شبكة من العناصر المتصلة. في العالم الرياضي، يُطلق على هذه البنية رسم بياني أو رسم بياني للذاكرة. يتم إنشاء الرسم البياني من عُقد متصلة بواسطة حواف، وتُعطى كلتاهما تسميات.
- يتم تصنيف العُقد (أو الكائنات) باستخدام اسم الدالة الدالة الإنشائية التي تم استخدامها لإنشائها.
- يتم تصنيف الحواف باستخدام أسماء الخصائص.
تعرّف على كيفية تسجيل ملف شخصي باستخدام أداة تحليل لقطات لأجزاء من الذاكرة. وتشمل بعض الأشياء اللافتة للانتباه التي يمكننا رؤيتها في تسجيل أداة تحليل لقطة لأجزاء من الذاكرة أدناه المسافة: المسافة من جذر تجميع البيانات المهملة. إذا كانت جميع الكائنات من النوع نفسه تقع على مسافة واحدة تقريبًا، وكانت بعضها على مسافة أكبر، فهذا شيء يستحق التحقيق فيه.
السيطرة
تتكوّن العناصر السائدة من بنية شجرة لأنّ كل كائن له عنصر سائد واحد بالضبط. قد يفتقر الشخص المهيمن على كائن ما إلى الإشارة المباشرة إلى الكائن الذي يسيطر عليه؛ أي أن شجرة الهيمنة ليست شجرة ممتدة من الرسم البياني.
في المخطط أدناه:
- تتحكم العقدة 1 في العقدة 2
- تقع العقدة 2 في العُقد 3 و4 و6
- تتحكم العقدة 3 في العقدة 5
- تتحكم العقدة 5 في العقدة 8
- تتحكم العقدة 6 في العقدة 7
في المثال أدناه، العقدة #3
هي السائدة على #10
، ولكن #7
توجد أيضًا في كل مسار بسيط بداية من تجميع البيانات المهملة إلى #10
. وبالتالي، يعد الكائن B هو العنصر الرئيسي للكائن A إذا كان موجودًا في كل مسار بسيط من الجذر إلى الكائن A.
تفاصيل V8
عند إنشاء ملفات تعريفية للذاكرة، من المفيد فهم سبب ظهور لقطات لأجزاء من الذاكرة بطريقة معيّنة. يصف هذا القسم بعض المواضيع المتعلّقة بالذاكرة والتي تتطابق تحديدًا مع الجهاز الافتراضي V8 JavaScript (V8 VM أو VM).
تمثيل عنصر JavaScript
هناك ثلاثة أنواع أساسية:
- أرقام (مثل 3.14159..)
- قيم منطقية (صواب أو خطأ)
- سلاسل (مثل "وارنر هايزنبرغ")
لا يمكن أن تشير إلى قيم أخرى وتكون دائمًا أوراق شجر أو عُقد منتهية.
يمكن تخزين الأرقام على النحو التالي:
- قيم عدد صحيح فوري 31 بت تُسمى الأعداد الصحيحة الصغيرة (SMI)، أو
- وحدات الذاكرة المتعددة، يُشار إليها باسم الأجزاء المتعددة. وتُستخدم أرقام الذاكرة لتخزين القيم التي لا تتناسب مع نموذج SMI، مثل القيم المزدوجة أو عندما يلزم وضع مربعًا في قيمة، مثل ضبط الخصائص عليها.
يمكن تخزين السلاسل في أي مما يلي:
- كومة ذاكرة الجهاز الافتراضي
- خارجيًا في ذاكرة العارض. يتمّ إنشاء كائن برنامج تضمين واستخدامه للوصول إلى وحدة التخزين الخارجية، حيث يتم مثلاً تخزين مصادر النصوص البرمجية والمحتوى الآخر الذي يتم تلقّيه من الويب، بدلاً من نسخه إلى كومة ذاكرة الجهاز الافتراضي (VM).
يتم تخصيص ذاكرة لكائنات JavaScript الجديدة من كومة JavaScript مخصّص (أو كومة ذاكرة افتراضية). تتم إدارة هذه الأجسام بواسطة أداة تجميع البيانات المهملة V8، وبالتالي ستظل حية ما دام هناك إشارة قوية واحدة إلى هذه الأجسام على الأقل.
الكائنات الأصلية هي كل العناصر الأخرى غير الموجودة في كومة JavaScript. وعلى عكس الكائن الأصلي، على عكس الكائن الأصلي لأجزاء من الذاكرة، لا تتم إدارته بواسطة أداة تجميع البيانات المهملة V8 طوال فترة بقائها، ولا يمكن الوصول إليه من JavaScript إلا باستخدام كائن برنامج تضمين JavaScript.
سلسلة السلبيات هي كائن يتكوّن من أزواج من السلاسل النصية ثم يتم ضمها وتكون نتيجة للتسلسل. يحدث ضم محتوى سلسلة السلبيات حسب الحاجة فقط. مثال على ذلك عندما تحتاج إلى إنشاء سلسلة فرعية من سلسلة مرتبطة.
على سبيل المثال، إذا أجريت تسلسلاً للحرفين a وb، ستحصل على سلسلة (a، b) تمثّل نتيجة السلسلة. إذا أجريت تسلسلاً لـ d مع هذه النتيجة، ستحصل على سلسلة سلبية أخرى ((a, b) وd).
المصفوفات - المصفوفة هي كائن يضم مفاتيح رقمية. وهي تُستخدم على نطاق واسع في الجهاز الافتراضي V8 لتخزين كميات كبيرة من البيانات. يتم الاحتفاظ بنسخة احتياطية من مجموعات أزواج المفتاح/القيمة المستخدمة مثل القواميس من خلال المصفوفات.
يمكن أن يكون كائن JavaScript النموذجي أحد نوعَي الصفائف المستخدمة للتخزين:
- والخصائص المُسماة
- العناصر الرقمية
وفي الحالات التي يتوفر فيها عدد قليل جدًا من السمات، يمكن تخزينها داخليًا في كائن JavaScript نفسه.
خريطة: كائن يصف نوع الكائن وتنسيقه. مثلاً، تُستخدم الخرائط لوصف تسلسلات هرمية للكائنات الضمنية من أجل الوصول السريع إلى الخصائص.
مجموعات الكائنات
تتألّف كل مجموعة كائنات أصلية من كائنات لها مراجع متبادلة بعضها مع بعض. على سبيل المثال، شجرة فرعية ضمن عناصر نموذج العناصر في المستند (DOM) تحتوي فيها كل عقدة على رابط يؤدي إلى العنصر الرئيسي ورابط إلى العنصر الثانوي التالي والعنصر الشقيق التالي، وبالتالي تكوين رسم بياني مرتبط. يُرجى العلم أنّ الكائنات الأصلية غير ممثلة في كومة JavaScript، ولهذا السبب لها حجم صفري. وبدلاً من ذلك، يتم إنشاء كائنات برنامج تضمين.
ويحمل كل كائن برنامج تضمين مرجعًا إلى الكائن الأصلي المقابل، لإعادة توجيه الأوامر إليه. تضم مجموعة الكائنات، بدورها، كائنات برنامج تضمين. ومع ذلك، لا يؤدي ذلك إلى إنشاء دورة غير قابلة للتجميع، لأنّ البيانات المجانية غير القابلة للتجميع (GC) ذكية بما يكفي لإصدار مجموعات الكائنات التي لم تعد تتم الإشارة إلى أغلفةها. لكن نسيان إصدار برنامج تضمين واحد سيحمل المجموعة بأكملها والأغلفة المرتبطة بها.