كيفية تخصيص المزيد من الكبش للصدأ


الاجابه 1:

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

لماذا لا يلزم تخصيص / إلغاء تخصيص الذاكرة للمكدس؟

مكدس الاستدعاءات ، يسمى عمومًا "المكدس" ، هو جزء أساسي من استدعاء الوظيفة. يتم استخدامه للاحتفاظ ، جيدًا ، بالمتغيرات المكدسة والحالة الحالية للمكالمة.

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

نوع من تخصيص الذاكرة

، ولكن التخصيص في المكدس بسيط للغاية وسريع جدًا مقارنة بالتخصيص المستند إلى الكومة ، لذلك نميل إلى القول بأنه لا يوجد "تخصيص" مطلوب.

أيضًا ، لماذا لا يستطيع المترجم معرفة مكان وجود الكائن في الكومة؟

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

كيف يمكن للمجمع معرفة مكان وجود الكائن إذا تم تخزينه في المكدس؟

سيتم دائمًا تخزين الكائنات الجديدة التي تنتمي إلى مكدس الاستدعاءات أعلى مكدس الاستدعاءات. تم تحسين هذا بشكل كبير لأن عمليات المكدس مدعومة أصلاً في معظم ، إن لم يكن كل ، أنواع وحدة المعالجة المركزية ، والتي توفر سجلًا داخليًا مخصصًا واحدًا على الأقل لـ "مؤشر المكدس" - المؤشر الذي يشير إلى أعلى مكدس الاستدعاءات. لا يعرف المترجم في الواقع مكان تخزين الكائن في المكدس ، ولكن يمكنه بالتأكيد إنتاج التعليمات للقيام بذلك.

تحذير كبير هو أنه بينما نحن على يقين من أن المكدس أسرع من الكومة في معظم الظروف ، فإننا نتحدث عن الاختلاف في النانو ثانية لكل تخصيص هنا. لا تحاول القيام بهذا النوع من التحسين لأي وظيفة تافهة.


الاجابه 2:

دعنا نحاول أن نشرح ، بلغة إنجليزية بسيطة ، ما هو المكدس وما هو الكومة.

المكدس هو ، حسنًا ، كومة من كتل الذاكرة ، يتم إنشاؤها عند استدعاء وظيفة. لذلك ، في كل مرة يستدعي برنامج C ++ وظيفة (أي وظيفة. حتى main ()) يخصص كتلة من الذاكرة على المكدس لهذه الوظيفة.

تحتوي كتلة الذاكرة هذه على كل متغير تم إنشاؤه على هذه الوظيفة ، بالإضافة إلى عنوان الوظيفة الأصلية التي أطلق عليها. عندما ينتهي تنفيذ هذه الوظيفة ، فإنها تقوم تلقائيًا بإلغاء تخصيص كتلة الذاكرة هذه ، مما يمسح بشكل فعال حتى المتغير الذي أنشأته الوظيفة بشكل ثابت (أي int ، char ، float ، أيًا كان). لذلك ، نظرًا لأن المجمعين يعرفون مسبقًا ، حيث سيتم وضع كل متغير تم إنشاؤه في الذاكرة ، فيمكنه إجراء الكثير من التحسينات.

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

دالة باطلة (int m) { int * v = (int *) malloc (m * sizeof (int)) ؛ // افعل شيئًا به}

إذا لم تتمكن من قراءة الكود ، فإنه يقوم ببساطة بإنشاء مصفوفة من ints ، بحجم m ، باستخدام مؤشر * v. في هذه الحالة ، لا يستطيع المترجم معرفة حجم المصفوفة مسبقًا ، لأن m لن يُعرف إلا عندما يكون البرنامج قيد التشغيل بالفعل. إذن ، كيف يعرف المترجم حجم كتلة الذاكرة لهذه الوظيفة في المكدس عندما يتم استدعاؤها؟

الجواب: لا. في هذه الحالة ، (وفي الواقع ، في أي حالة أخرى تستخدم فيها المؤشرات ، حتى لو كانت ضمنية ، كما هو الحال في ناقل std :: ) ، لن يتم تخزين المصفوفة الفعلية في المكدس ، ولكن في الكومة ، هذه منطقة مختلفة من الذاكرة ، مستقلة عن المكدس. لذلك ، سيخصص المترجم ذاكرة كافية فقط لـ POINTER ، وليس البيانات (في هذه الحالة ، من المحتمل أن يكون عدد صحيح 32 بت) ، وستعيش البيانات نفسها في جزء مختلف من الذاكرة ، مع تخزين العنوان في المؤشر ( بالنسبة إلى الأصوليين ، يكون المؤشر هو عنوان العنصر الأول من المصفوفة). تكمن المشكلة في أنه عندما تعود الدالة ، لا تحتوي على أي معلومات عن المصفوفة نفسها ، لذا فإن إلغاء تخصيصها هو واجب المبرمج ، ولا يستطيع المترجم تحسين هذه العملية بشكل فعال. إذا لم يقم المبرمج بذلك بالطريقة الصحيحة ، سيكون هناك تسرب للذاكرة.

الجزء الجيد منه هو أنه نظرًا لأن البيانات ليست في المكدس ، ولكن في الكومة ، فلن يتم إتلافها بمجرد عودة الوظيفة. لذلك ، يمكنك بسهولة تمرير مؤشر البيانات إلى وظائف أخرى ، والتي يمكنها الآن الوصول إلى هذه البيانات الموجودة في الكومة.

كل هذه المحادثة صحيحة ، بشكل أساسي ، بالنسبة لـ C. العادي ولكن ، وفقًا للعديد من هياكل البيانات على C ++ ، يحدث نفس الشيء. على سبيل المثال ، سيخصص C ++ std :: vector كل عنصر من المتجه على الكومة ، وليس على المكدس ، لأنه لا توجد طريقة لمعرفة عدد العناصر التي قد يحصل عليها المتجه في النهاية مسبقًا.

ربما يكون هناك عيبان في توضيحي. لم أقم بتثبيته ، لكن الفكرة الرئيسية هي هذا.

tl ؛ dr: يتم تخصيص المتغيرات المحلية لوظيفة ما في المكدس ، ويتم إلغاء تخصيصها تلقائيًا عند إرجاع الدالة. يتم تخصيص المتغيرات التي لا يستطيع المترجم أن يستنتج الحجم مسبقًا (مثل المتجهات) أو تلك المؤشرات (التي من المحتمل أن تستخدمها وظائف أخرى) في الكومة.


الاجابه 3:

السبب الرئيسي هو أن المكدس يناسب ذاكرة التخزين المؤقت لوحدة المعالجة المركزية المحلية.

تخيل إعلان int i؛ متغير؛ قل ، في حلقة for-loop بسيطة ، مثل for (int i = 0 ؛ i

حتى شخص جافا المتشدد سيدرك أنه من خلال تخصيص i "خارجيًا" (بشكل فعال ، عن طريق جعل Int i = new Int () ؛ call) ، فإنك تهدر الموارد.

الآن ، لنفترض أن لديك فئة قليلة - أو حتى واحدة فقط - عدد صحيح كبير. لماذا تريد تخصيصه على الأرض باستخدام الجديد ، إذا كانت هناك طريقة سهلة؟

هذه الطريقة السهلة ممكنة - ويتم تشجيعها على نطاق واسع - في C ++ لأنه بدلاً من جمع القمامة القائم على العد المرجعي ، تستخدم C ++ إدارة عمر الكائن المستندة إلى النطاق. يعرف المترجم أنه مع انتهاء النطاق الحالي ، يجب التخلص من الكائنات المخصصة فيه تلقائيًا. وبالتالي ، فإن تخطيط الذاكرة للكائنات المخصصة محليًا يتطابق بشكل ملائم مع كيفية عمل المكدس لاستدعاءات الوظائف المتداخلة.

هذه الراحة ليست مصادفة بالطبع. إنها فلسفة كيفية تعامل C ++ مع عمر الكائن.

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

(هذا ، بالمناسبة ، هو أحد الأخطاء الشائعة في C ++: تمرير مؤشر / مرجع إلى كائن مخصص مكدس إلى مكالمة غير متزامنة دون التأكد من أن إطار المكدس المخصص له سيعيش طويلاً بما يكفي. العقود الآجلة ووعود FTW في هذه الحالة. )

الاتجاه الصعودي: الأداء والجمال الهندسي والتنبؤ الرائع لإدارة ملكية الكائنات.

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

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

أوه ، وهل ذكرت أن هذا التخصيص المستند إلى النطاق يجعل البناء النهائي غير ضروري على الإطلاق؟ حسنًا ، إنها كذلك.


الاجابه 4:

يمكن أن يكون الأمر كذلك (على الرغم من أنك غالبًا لا تملك خيارًا في Java في هذه المسألة).

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

تمرين: قم بتجميع برنامج C ++ باستخدام تخصيص وأخرى باستخدام malloc ، وانظر إلى الاختلافات في الكود الناتج.

الآن ، في نظام جمع القمامة ، قد يكون التخصيص سريعًا تقريبًا من الكومة ، لأنه يمكن تنظيم الكومة باعتبارها

مخصص المنطقة

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

بالإضافة إلى ذلك ، قد تعمل منطقة الذاكرة لصالح التخصيص من المكدس بدلاً من الكومة. قد تكون الذاكرة المستخدمة لمكدس مؤشرات الترابط موجودة بالفعل في ذاكرة التخزين المؤقت لوحدة المعالجة المركزية L1 أو L2. من غير المحتمل أيضًا أن يكون لديك أي سلوك 'ping pong' مع وحدة معالجة مركزية أخرى ، حيث لن يكون لأي مؤشر ترابط آخر سبب للوصول إلى مكدس مؤشر الترابط الحالي.


الاجابه 5:

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

يتم الاحتفاظ بذاكرة الكومة منفصلة عن ذاكرة المكدس ، لذلك لا داعي للقلق بشأن الالتفاف على بعضها البعض.

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

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


الاجابه 6:
يكون المكدس أسرع من الكومة لأنه يتم ضمان تحرير ذاكرة المكدس بالترتيب العكسي المخصص لها. هذا يجعل من السهل إدارتها (لا داعي لدمج المناطق الحرة على سبيل المثال) ويحسن موقع الوصول إلى الذاكرة.

الاجابه 7:

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


الاجابه 8:

معظم الإجابات عالقة في التجريدات.

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

من ناحية أخرى ، تتم إدارة تخصيص الكومة بواسطة نظام التشغيل ، أو بعض التجريد الآخر الذي "يدير" الذاكرة.

ما هو الأسرع - تعليمات وحدة معالجة مركزية واحدة ، أم مجموعة العمليات التي تتم عن طريق استدعاء خوارزمية إدارة ذاكرة نظام التشغيل؟ ؛)

تتم عمليات تخصيص الكومة في وقت التشغيل ، لذلك لا توجد طريقة للمترجم لمعرفة مكان تخصيص شيء ما في الكومة.


الاجابه 9:

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