هل تحتوي جافا سكريبت على تسرب للذاكرة ؟ إذا كان الأمر كذلك، وكيفية تجنب ذلك؟ نظرًا لحقيقة أن العديد من الأشخاص قد طرحوا علي أسئلة مماثلة مؤخرًا، يبدو أنه لم يدرس أحد هذا الجزء من المحتوى بشكل منهجي، لذلك أخطط لمشاركة بعض المعلومات التي قمت بتجميعها قبل بضع سنوات.
بادئ ذي بدء، يمكن القول على وجه اليقين أن بعض طرق كتابة JavaScript ستتسبب في حدوث تسرب للذاكرة، على الأقل في IE6. لذلك، اليوم عندما يرفض IE6 التقاعد، ما زلنا بحاجة إلى فهم المعرفة ذات الصلة (على الرغم من أن تسرب الذاكرة الناجم عن JS ليس هو السبب الرئيسي لإبطاء الكمبيوتر في معظم الحالات). تتركز الأبحاث ذات الصلة بشكل أساسي في الأعوام 2005-2007. لا تحتوي هذه المقالة على أي أفكار جديدة. إذا كان لديك أصدقاء درسوها في ذلك الوقت، فيمكنك تجاهلها.
كمطور للواجهة الأمامية، عند فهم هذه المشكلات، عليك أن تعرف ما هي ولماذا، لذلك، قبل تقديم تسرب ذاكرة JS، لنبدأ مع سبب وجود تسرب للذاكرة.
عند الحديث عن تسرب الذاكرة، علينا أن نتحدث عن طريقة تخصيص الذاكرة. هناك ثلاث طرق لتخصيص الذاكرة، وهي:
1. التخصيص الثابت: نموذج تخصيص المتغيرات الثابتة والمتغيرات العامة. إذا فكرنا في الغرفة كبرنامج، فيمكننا أن نفكر في الذاكرة المخصصة بشكل ثابت كأثاث متين في الغرفة. عادةً، لا يلزم إطلاقها وإعادة تدويرها لأنه لا أحد يرمي الخزائن من النافذة كقمامة كل يوم.
2. التخصيص التلقائي: طريقة لتخصيص الذاكرة للمتغيرات المحلية على المكدس. يمكن تحرير الذاكرة الموجودة في المكدس تلقائيًا من خلال عملية البوب عند خروج كتلة التعليمات البرمجية.
وهذا مشابه للأشخاص الذين يأتون إلى الغرفة للقيام بالأشياء، وبمجرد الانتهاء من الأشياء، سيغادرون بمفردهم، وسيتم تحرير المساحة التي يشغلونها تلقائيًا عندما يغادر هؤلاء الأشخاص.
3. التخصيص الديناميكي: طريقة لتخصيص مساحة الذاكرة ديناميكيًا في الكومة لتخزين البيانات. أي أن الذاكرة المستخدمة لاستخدام malloc أو الجديدة عند تشغيل البرنامج، نحتاج إلى تحريرها بأنفسنا باستخدام free أوdelete. يتم تحديد عمر الذاكرة الديناميكية بواسطة المبرمج. بمجرد أن تنسى تحريره، فإنه سيؤدي حتماً إلى تسرب الذاكرة. في هذه الحالة، تكون كتل الذاكرة الموجودة في الكومة مثل المناديل التي نستخدمها كل يوم، بعد استخدامها، يتعين علينا رميها في سلة المهملات، وإلا سيكون المنزل في حالة من الفوضى. ولذلك، يحلم الأشخاص الكسالى بامتلاك روبوت منزلي للتنظيف معهم. في تطوير البرمجيات، إذا كنت كسولًا جدًا بحيث لا يمكنك تحرير الذاكرة، فأنت بحاجة أيضًا إلى روبوت مماثل - وهو في الواقع أداة تجميع البيانات المهملة التي يتم تنفيذها بواسطة خوارزمية محددة. إن بعض العيوب في آلية جمع البيانات المهملة نفسها هي التي تؤدي إلى تسرب ذاكرة JavaScript.
منذ عدة سنوات، قرأت مقالًا بعنوان "تاريخ مثير للاهتمام لإعادة تدوير القمامة"، والذي قدم شرحًا متعمقًا لآلية جمع القمامة.
تمامًا مثل الشحن الفائق، وهي تقنية تستخدمها العديد من السيارات الفاخرة كنقطة بيع، والتي كانت تستخدمها مرسيدس بنز بالفعل في العقد الأول من القرن العشرين، كانت تكنولوجيا إعادة تدوير القمامة موجودة منذ فترة طويلة. كانت لغة Lisp، التي ولدت في معهد ماساتشوستس للتكنولوجيا حوالي عام 1960، هي اللغة الأولى التي اعتمدت بشكل كبير على تقنية تخصيص الذاكرة الديناميكية. تظهر جميع البيانات الموجودة في Lisp تقريبًا في شكل "جداول"، والمساحة التي تشغلها "الجداول" موجودة في الكومة. المخصصة ديناميكيا. تتطلب ميزة إدارة الذاكرة الديناميكية الفطرية في لغة Lisp من مصممي لغة Lisp حل مشكلة الإصدار التلقائي لكل كتلة ذاكرة في الكومة (وإلا، فإن مبرمجي Lisp سوف يغمرهم حتمًا عدد لا يحصى من البيانات المجانية أو المحذوفة في البرنامج). أدى هذا بشكل مباشر إلى ولادة وتطوير تكنولوجيا جمع القمامة.
ظهرت أيضًا الخوارزميات الثلاثة الأساسية لجمع البيانات المهملة معًا في ذلك الوقت. دعونا نلقي نظرة عليها واحدا تلو الآخر:
خوارزمية العد المرجعي: قد تكون هذه هي الطريقة الأولى التي تتبادر إلى الذهن. وبعبارة مجازية، يمكن فهم العد المرجعي بهذه الطريقة. هناك الكثير من الأوراق البيضاء في المنزل، وهذه الأوراق تشبه الذكريات. إن استخدام الذاكرة يشبه الكتابة على هذه القطع من الورق. يمكن استخدام الذاكرة حسب الرغبة، ولكن هناك شرط. يجب على أي شخص يستخدم قطعة من الورق أن يكتب العد 1 على زاوية الورقة. إذا استخدم شخصان قطعة من الورق في نفس الوقت، يصبح العد 2، وهكذا. عندما ينتهي الشخص من استخدام قطعة من الورق، يجب تقليل العدد الموجود على الزاوية بمقدار 1. وبهذه الطريقة، بمجرد أن يصبح العدد 0، يتم استيفاء شروط جمع البيانات المهملة، وسيقوم الروبوت الذي ينتظر جانبًا بإلقاء الورقة على الفور في الورقة. صندوق القمامة. يعمل مجمع البيانات المهملة المرجعي القائم على العداد بشكل أسرع، ولا يقاطع تنفيذ البرنامج لفترة طويلة، وهو مناسب للبرامج التي يجب تشغيلها في الوقت الفعلي. ومع ذلك، فإن العداد المرجعي يزيد من الحمل الزائد لتنفيذ البرنامج، وفي الوقت نفسه، هناك مشكلة أكبر أخرى في هذه الخوارزمية، وهي أنه بمجرد إنشاء مرجع دائري، سيتم تسريب الذاكرة. على سبيل المثال، قمنا بإضافة كائنين جديدين a وb في هذا الوقت، حيث يكون عدد كل من a وb هو 1. ثم نشير إلى سمة من a إلى b، وسمة من b إلى a في هذا الوقت، بسبب العلاقة المرجعية، يصبح عدد a وb 2. عندما ينتهي البرنامج ويخرج من النطاق، يقوم البرنامج تلقائيًا بتخفيض عدد a بمقدار 1. نظرًا لأن عدد a في النهاية لا يزال 1، فلن يكون وبالمثل، فإن العدد النهائي لـ b هو أيضًا 1، ولن يتم تحرير b، ويتم تسريب الذاكرة فقط!