جمع البيانات المهملة هي آلية مخفية في JavaScript
، لا داعي للقلق عادةً بشأن جمع البيانات المهملة، نحتاج فقط إلى التركيز على تطوير الوظائف. لكن هذا لا يعني أنه يمكننا الجلوس والاسترخاء عند كتابة JavaScript
، فعندما تصبح الوظائف التي ننفذها أكثر تعقيدًا وتراكم كمية التعليمات البرمجية، تصبح مشكلات الأداء أكثر وضوحًا. إن كيفية كتابة التعليمات البرمجية التي يتم تنفيذها بشكل أسرع وتستهلك ذاكرة أقل هي السعي الذي لا ينتهي للمبرمجين. يمكن للمبرمج الممتاز دائمًا تحقيق نتائج مذهلة بموارد محدودة للغاية. وهذا أيضًا هو الفرق بين الكائنات العادية والآلهة المنعزلة.
؟ عند التنفيذ في ذاكرة الكمبيوتر، ستشغل جميع المتغيرات والكائنات والوظائف التي نحددها في الكود قدرًا معينًا من مساحة الذاكرة في الذاكرة. في أجهزة الكمبيوتر، تعد مساحة الذاكرة موردًا ضيقًا للغاية، ويجب علينا دائمًا الانتباه إلى استخدام الذاكرة، فوحدات الذاكرة مكلفة للغاية. يمكن تسمية المتغير أو الوظيفة أو الكائن بالقمامة إذا لم تعد هناك حاجة إليها لتنفيذ التعليمات البرمجية اللاحقة بعد الإنشاء.
على الرغم من أنه من السهل جدًا فهم تعريف القمامة بشكل حدسي، إلا أنه بالنسبة لبرنامج الكمبيوتر، يصعب علينا أن نستنتج في لحظة معينة أن المتغيرات أو الوظائف أو الكائنات الموجودة حاليًا لن يتم استخدامها في المستقبل. من أجل تقليل تكلفة ذاكرة الكمبيوتر وضمان التنفيذ الطبيعي لبرامج الكمبيوتر، نشترط عادةً أن الكائنات أو المتغيرات التي تستوفي أيًا من الشروط التالية هي كائنات غير مرغوب فيها:
تعادل المتغيرات أو الكائنات التي لم يتم الرجوع إليها منزلًا بدون باب، ولا يمكننا الدخول إليه أبدًا، لذلك من المستحيل استخدامه. على الرغم من أن الكائنات التي يتعذر الوصول إليها متصلة، إلا أنه لا يزال يتعذر الوصول إليها من الخارج وبالتالي لا يمكن استخدامها مرة أخرى. لن يتم استخدام الكائنات أو المتغيرات التي تستوفي الشروط المذكورة أعلاه مرة أخرى أبدًا في التنفيذ المستقبلي للبرنامج، لذلك يمكن معاملتها بأمان على أنها مجموعة من البيانات المهملة.
عندما نوضح الكائنات التي يجب التخلص منها من خلال التعريف أعلاه، هل يعني ذلك أنه لا يوجد أي قمامة في المتغيرات والكائنات المتبقية؟
لا! القمامة التي نحددها حاليًا ليست سوى جزء من كل القمامة، وستظل هناك قمامة أخرى لا تستوفي الشروط المذكورة أعلاه، ولكن لن يتم استخدامها مرة أخرى.
هل يمكن القول أن القمامة التي تنطبق عليها التعريف أعلاه هي "قمامة مطلقة" والقمامة الأخرى المخفية في البرنامج هي "قمامة نسبية"؟
آلية تجميع البيانات المهملة ( GC,Garbage Collection
) مسؤولة عن إعادة تدوير المتغيرات غير المفيدة ومساحة الذاكرة المشغولة أثناء تنفيذ البرنامج. وتسمى ظاهرة استمرار كائن ما في الذاكرة على الرغم من عدم إمكانية استخدامه مرة أخرى بتسرب الذاكرة . يعد تسرب الذاكرة ظاهرة خطيرة جدًا، خاصة في البرامج طويلة التشغيل. إذا كان أحد البرامج يعاني من تسرب للذاكرة، فسوف يشغل المزيد والمزيد من مساحة الذاكرة حتى نفاد الذاكرة.
ليس للسلاسل والكائنات والمصفوفات حجم ثابت، لذلك لا يمكن إجراء تخصيص التخزين الديناميكي لها إلا إذا كان حجمها معروفًا. في كل مرة يقوم فيها برنامج JavaScript بإنشاء سلسلة أو مصفوفة أو كائن، يقوم المترجم بتخصيص الذاكرة لتخزين الكيان. عندما يتم تخصيص الذاكرة ديناميكيًا بهذه الطريقة، يجب تحريرها في النهاية حتى يمكن استخدامها مرة أخرى؛ وإلا فإن مترجم JavaScript سوف يستهلك كل الذاكرة المتوفرة في النظام، مما يتسبب في تعطل النظام.
ستقوم آلية جمع البيانات المهملة في JavaScript
بالتحقق بشكل متقطع من المتغيرات والكائنات غير المفيدة (البيانات المهملة) وتحرير المساحة التي تشغلها.
تعتمد لغات البرمجة المختلفة استراتيجيات مختلفة لجمع البيانات المهملة، على سبيل المثال، لا تحتوي C++
على آلية لجمع البيانات المهملة. تعتمد إدارة الذاكرة بالكامل على مهارات المبرمج الخاصة، مما يجعل إتقان C++
أكثر صعوبة. تستخدم JavaScript
إمكانية الوصول لإدارة الذاكرة. تعني إمكانية الوصول حرفيًا أنه يمكن الوصول إلى المتغيرات والكائنات واستخدامها بطريقة ما.
تحدد JavaScript
مجموعة متأصلة من القيم التي يمكن الوصول إليها، والقيم الموجودة في المجموعة قابلة للوصول بطبيعتها:
تسمى الجذور ، وهي العقد العليا لشجرة إمكانية الوصول.
يعتبر المتغير أو الكائن قابلاً للوصول إذا تم استخدامه بشكل مباشر أو غير مباشر بواسطة المتغير الجذر.
بمعنى آخر، يمكن الوصول إلى القيمة إذا كان من الممكن الوصول إليها من خلال الجذر (على سبيل المثال، Abcde
).
دع الأشخاص = { الأولاد:{ الأولاد1:{الاسم:'شياومينغ'}، الأولاد 2: {الاسم:'شياوجون'}، }, فتيات:{ الفتيات 1:{الاسم:'شياوهونغ'}، البنات2:{الاسم:'هواهوا'}, }};
يقوم الكود أعلاه بإنشاء كائن وتخصيصه للمتغير people
. يحتوي المتغير people
على كائنين، boys
girls
، ويحتوي boys
girls
على كائنين فرعيين على التوالي. يؤدي هذا أيضًا إلى إنشاء بنية بيانات تحتوي على 3
مستويات من العلاقات المرجعية (بغض النظر عن نوع البيانات الأساسية)، كما هو موضح أدناه:
من بينها، يمكن الوصول إلى عقدة people
بشكل طبيعي لأنها متغير عالمي. يمكن الوصول إلى عقد boys
girls
بشكل غير مباشر لأنه يتم الرجوع إليها مباشرة بواسطة المتغيرات العالمية. تعتبر boys1
و boys2
و girls1
و girls2
أيضًا متغيرات يمكن الوصول إليها لأنها تستخدم بشكل غير مباشر بواسطة المتغيرات العالمية ويمكن الوصول إليها من خلال people.boys.boys
.
إذا أضفنا الكود التالي بعد الكود أعلاه:
people.girls.girls2 = null;people.girls.girls1 =people.boys.boys2،
فسيصبح المخطط الهرمي المرجعي أعلاه كما يلي:
ومن بينها، أصبحت girls1
girls2
عقدتين لا يمكن الوصول إليهما بسبب الانفصال عن عقدة grils
، مما يعني أنه سيتم إعادة تدويرهما بواسطة آلية جمع القمامة.
وإذا قمنا في هذا الوقت بتنفيذ الكود التالي:
people.boys.boys2 = null؛
فسيصبح مخطط التسلسل الهرمي المرجعي كما يلي:
في هذا الوقت، على الرغم من فصل عقدة boys
وعقدة boys2
، نظرًا للعلاقة المرجعية بين عقدة boys2
وعقدة girls
، لا يزال من الممكن الوصول إلى boys2
ولن تتم إعادة تدويرهم بواسطة آلية جمع القمامة.
يوضح مخطط الارتباط أعلاه سبب تسمية القيمة المكافئة للمتغير العام بالجذر ، لأنه في مخطط الارتباط، يظهر هذا النوع من القيمة عادةً كعقدة جذر لشجرة العلاقة.
دع الناس = { الأولاد:{ الأولاد1:{الاسم:'شياومينغ'}، الأولاد 2: {الاسم:'شياوجون'}، }, فتيات:{ الفتيات 1:{الاسم:'شياوهونغ'}، البنات2:{الاسم:'هواهوا'}, }};people.boys.boys2. Girlfriend = People.girls.girls1; يشير //boys2 إلى Girls1people.girls.girls1.boyfriend =people.boys.boys2; //girls1 يشير إلى boys2.
ينشئ الكود أعلاه علاقة مترابطة بين boys2
و girls1
، ويكون مخطط هيكل العلاقة كما يلي:
في هذه المرحلة، إذا قطعنا الارتباط بين boys
boys2
:
احذف People.boys.boys2؛
ويكون مخطط الارتباط بين الكائنات كما يلي:
من الواضح أنه لا توجد عقد لا يمكن الوصول إليها.
في هذه المرحلة، إذا قطعنا اتصال علاقة boyfriend
:
احذف People.girls.girls1؛
يصبح مخطط العلاقة:
في هذا الوقت، على الرغم من أنه لا تزال هناك علاقة girlfriend
بين boys2
girls1
، يصبح boys2
عقدة لا يمكن الوصول إليها وسيتم استعادتها من خلال آلية جمع القمامة.
دع الناس = { الأولاد:{ الأولاد1:{الاسم:'شياومينغ'}، الأولاد 2: {الاسم:'شياوجون'}، }, فتيات:{ الفتيات 1:{الاسم:'شياوهونغ'}، البنات2:{الاسم:'هواهوا'}, }};deletepeople.boys;deletepeople.girls;
الرسم التخطيطي للتسلسل الهرمي المرجعي الذي يتكون من الكود أعلاه هو كما يلي:
في الوقت الحالي، على الرغم من أنه لا تزال هناك علاقة مرجعية متبادلة بين الكائنات الموجودة داخل المربع المنقط، إلا أن هذه الكائنات أيضًا غير قابلة للوصول وسيتم حذفها بواسطة آلية جمع البيانات المهملة. فقدت هذه العقد علاقتها بالجذر وأصبحت غير قابلة للوصول.
البيانات المهملة ما يسمى بـ عد المرجع، كما يوحي الاسم، يتم حسابه في كل مرة تتم فيها الإشارة إلى كائن. ستؤدي إضافة مرجع إلى زيادته بمقدار واحد، وسيؤدي حذف المرجع إلى تقليله بمقدار واحد يصبح الرقم 0، ويعتبر غير مرغوب فيه، وبالتالي حذف الكائنات لاستعادة الذاكرة.
على سبيل المثال:
Let user = {username:'xiaoming'}; // تتم الإشارة إلى الكائن بواسطة متغير المستخدم، العد +1 دع المستخدم 2 = المستخدم؛ // تتم الإشارة إلى الكائن بواسطة متغير جديد، والعدد +1 المستخدم = فارغ؛ // لم يعد المتغير يشير إلى الكائن، والعدد هو -1 user2 = null; // لم يعد المتغير يشير إلى الكائن، الرقم الفردي -1 // في هذا الوقت، عدد مراجع الكائنات هو 0 وسيتم حذفها.
على الرغم من أن طريقة حساب المراجع تبدو معقولة جدًا، إلا أن هناك في الواقع ثغرات واضحة في آلية إعادة تدوير الذاكرة باستخدام طريقة حساب المراجع.
على سبيل المثال:
Let boy = {}; دع الفتاة = {}؛ صبي.صديقة = فتاة؛ فتاة.صديقها = ولد؛ صبي = فارغ؛ Girl = null؛
يحتوي الكود أعلاه على مراجع متبادلة بين boy
girl
. يؤدي العد إلى حذف المراجع الموجودة في boy
girl
، ولن يتم إعادة تدوير العنصرين. نظرًا لوجود مراجع دائرية، لن يعود عدد المراجع للكائنين المجهولين أبدًا إلى الصفر، مما يؤدي إلى تسرب الذاكرة.
يوجد مفهوم المؤشر الذكي ( shared_ptr
) في C++
. يمكن للمبرمجين استخدام المؤشر الذكي لاستخدام أداة تدمير الكائنات لتحرير العدد المرجعي. ومع ذلك، سيحدث تسرب للذاكرة في حالة المراجع الدائرية.
لحسن الحظ، اعتمدت JavaScript
استراتيجية أخرى أكثر أمانًا، والتي تتجنب خطر تسرب الذاكرة إلى حد كبير.
هي mark and sweep
لجمع البيانات المهملة يعتمدها محرك JavaScript
، ومبدأها الأساسي هو البدء من الجذر ، واجتياز العلاقة المرجعية بين المتغيرات بالعرض أولاً، ووضع علامة (优秀员工徽章
) على المتغيرات التي تم اجتيازها. يتم حذف الكائنات غير المميزة أخيرًا.
العملية الأساسية للخوارزمية هي كما يلي:
2
حتى لا ينضم موظفون ممتازون جدد،على سبيل المثال:
إذا كانت هناك علاقة مرجعية للكائن في برنامجنا كما هو موضح أدناه:
يمكننا أن نرى بوضوح أن هناك "جزيرة يمكن الوصول إليها" على الجانب الأيمن من الصورة بأكملها، بدءًا من الجذر ، ولا يمكن الوصول إلى الجزيرة أبدًا. لكن جامعي البيانات المهملة ليس لديهم منظور إلهي مثل منظورنا، وسوف يقومون فقط بتمييز العقدة الجذرية كموظف متميز بناءً على الخوارزمية.
ثم ابدأ من الموظفين المتميزين وابحث عن جميع العقد التي ذكرها الموظفون المتميزون، مثل العقد الثلاث في المربع المنقط في الشكل أعلاه. ثم قم بتمييز العقد التي تم العثور عليها حديثًا كموظفين متميزين.
يتم تكرار عملية البحث ووضع العلامات حتى يتم تحديد جميع العقد التي يمكن العثور عليها بنجاح.
وأخيرا، يتم تحقيق التأثير المبين في الشكل أدناه:
نظرًا لأن الجزر الموجودة على اليمين لا تزال غير مميزة بعد انتهاء دورة تنفيذ الخوارزمية، فلن يتمكن مهمة جامع البيانات المهملة من الوصول إلى هذه العقد وسيتم مسحها في النهاية.
قد يتفاجأ الأطفال الذين درسوا هياكل البيانات والخوارزميات عندما يجدون أن هذا هو اجتياز الرسم البياني، على غرار خوارزميات الرسم البياني المتصلة.
تعد عملية جمع البيانات المهملة مهمة واسعة النطاق، خاصةً عندما تكون كمية التعليمات البرمجية كبيرة جدًا، فإن التنفيذ المتكرر لخوارزمية جمع البيانات المهملة سيؤدي إلى تأخير تنفيذ البرنامج بشكل كبير. أجرت خوارزمية JavaScript
الكثير من التحسينات في جمع البيانات المهملة لضمان إمكانية تنفيذ البرنامج بكفاءة مع ضمان التنفيذ الطبيعي لأعمال إعادة التدوير.
تتضمن الاستراتيجيات المعتمدة لتحسين الأداء عادةً النقاط التالية:
ستحافظ برامج JavaScript
على عدد كبير من المتغيرات أثناء التنفيذ، وسيتسبب المسح المتكرر لهذه المتغيرات في زيادة النفقات العامة. ومع ذلك، فإن هذه المتغيرات لها خصائصها الخاصة في دورة الحياة، على سبيل المثال، يتم إنشاء المتغيرات المحلية بشكل متكرر، واستخدامها بسرعة، ثم يتم التخلص منها، بينما تشغل المتغيرات العامة الذاكرة لفترة طويلة. تدير JavaScript
كلا النوعين من الكائنات بشكل منفصل. بالنسبة للمتغيرات المحلية التي يتم إنشاؤها واستخدامها والتخلص منها بسرعة، سيقوم جامع البيانات المهملة بفحصها بشكل متكرر للتأكد من تنظيف هذه المتغيرات بسرعة بعد أن تفقد استخدامها. بالنسبة للمتغيرات التي تحتفظ بالذاكرة لفترة طويلة، قم بتقليل تكرار التحقق منها، وبالتالي توفير قدر معين من النفقات العامة.
تعتبر الفكرة التزايدية شائعة جدًا في تحسين الأداء ويمكن استخدامها أيضًا لجمع البيانات المهملة. عندما يكون عدد المتغيرات كبيرًا جدًا، فمن الواضح أن اجتياز جميع المتغيرات مرة واحدة وإصدار علامات الموظفين المتميزة يستغرق وقتًا طويلاً للغاية، مما يؤدي إلى حدوث تأخيرات أثناء تنفيذ البرنامج. لذلك، سيقوم المحرك بتقسيم عمل جمع البيانات المهملة إلى مهام فرعية متعددة، وتنفيذ كل مهمة صغيرة تدريجيًا أثناء تنفيذ البرنامج، مما سيؤدي إلى تأخير معين في الاسترداد، لكنه لن يتسبب عادةً في تأخير واضح للبرنامج.
لا تعمل وحدةCPU
حتى في البرامج المعقدة، ويرجع ذلك أساسًا إلى أن CPU
تعمل بسرعة كبيرة وأن IO
الطرفية غالبًا ما تكون أبطأ بعدة أوامر من حيث الحجم، لذلك من الجيد ترتيب استراتيجية تجميع البيانات المهملة عندما تكون CPU
كذلك idle هذه طريقة فعالة جدًا لتحسين الأداء ولن يكون لها أي آثار سلبية على البرنامج نفسه. تشبه هذه الإستراتيجية ترقية وقت الخمول للنظام، ولا يكون المستخدمون على علم بالتنفيذ في الخلفية على الإطلاق.
المهمة الرئيسية لهذه المقالة هي ببساطة إنهاء آليات جمع البيانات المهملة والاستراتيجيات شائعة الاستخدام وطرق التحسين. وليس المقصود منها إعطاء الجميع فهمًا متعمقًا لمبادئ التنفيذ الخلفية للمحرك.
من خلال هذه المقالة يجب أن تفهم:
JavaScript
، والتي يتم تنفيذها في الخلفية ولا تتطلب منا القلق بشأنها؛