سؤال

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

  • أين وما هم (جسديًا في ذاكرة الكمبيوتر الحقيقية)؟
  • إلى أي مدى يتم التحكم فيها بواسطة نظام التشغيل أو وقت تشغيل اللغة؟
  • ما هو نطاقهم؟
  • ما الذي يحدد حجم كل منهم؟
  • ما الذي يجعل المرء أسرع؟
هل كانت مفيدة؟

المحلول

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

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

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

للإجابة على أسئلتك مباشرة:

إلى أي مدى يتم التحكم فيها بواسطة نظام التشغيل أو وقت تشغيل اللغة؟

يقوم نظام التشغيل بتخصيص المكدس لكل مؤشر ترابط على مستوى النظام عند إنشاء مؤشر الترابط.عادةً ما يتم استدعاء نظام التشغيل بواسطة وقت تشغيل اللغة لتخصيص الكومة للتطبيق.

ما هو نطاقهم؟

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

ما الذي يحدد حجم كل منهم؟

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

ما الذي يجعل المرء أسرع؟

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

مظاهرة واضحة:
مصدر الصورة: vikashazrati.wordpress.com

نصائح أخرى

كومة:

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

كومة:

  • يتم تخزينها في ذاكرة الوصول العشوائي للكمبيوتر تمامًا مثل المكدس.
  • في C++، يجب تدمير المتغيرات الموجودة في الكومة يدويًا وعدم الخروج عن النطاق أبدًا.يتم تحرير البيانات مع delete, delete[], ، أو free.
  • أبطأ في التخصيص مقارنة بالمتغيرات الموجودة على المكدس.
  • يستخدم عند الطلب لتخصيص كتلة من البيانات ليستخدمها البرنامج.
  • يمكن أن يكون هناك تجزئة عندما يكون هناك الكثير من التخصيصات والتخصيصات.
  • في C++ أو C، ستتم الإشارة إلى البيانات التي تم إنشاؤها على الكومة بواسطة المؤشرات وتخصيصها new أو malloc على التوالى.
  • يمكن أن يحدث فشل في التخصيص إذا طُلب تخصيص مخزن مؤقت كبير جدًا.
  • يمكنك استخدام الكومة إذا كنت لا تعرف بالضبط مقدار البيانات التي ستحتاجها في وقت التشغيل أو إذا كنت بحاجة إلى تخصيص الكثير من البيانات.
  • المسؤول عن تسرب الذاكرة.

مثال:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

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

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

    Stack like a stack of papers

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

  • في الكومة، لا يوجد ترتيب معين لطريقة وضع العناصر.يمكنك الوصول إلى العناصر وإزالتها بأي ترتيب لأنه لا يوجد عنصر "أعلى" واضح.

    Heap like a heap of licorice allsorts

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

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

  • إلى أي مدى يتم التحكم فيها بواسطة نظام التشغيل أو وقت تشغيل اللغة؟

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

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

  • ما هو نطاقهم؟

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

  • ما الذي يحدد حجم كل منهم؟

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

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

  • ما الذي يجعل المرء أسرع؟

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

(لقد قمت بنقل هذه الإجابة من سؤال آخر كان إلى حد ما خدعة لهذا السؤال.)

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

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

الكومة

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

The heap

المدخنة

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

The stack

هل يمكن تخصيص وظيفة على الكومة بدلاً من المكدس؟

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

إن كيفية إدارة الكومة أمر متروك لبيئة التشغيل.استخدامات ج malloc واستخدامات C++ new, ، لكن العديد من اللغات الأخرى لديها خاصية جمع البيانات المهملة.

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

في كود C# التالي

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

وإليك كيفية إدارة الذاكرة

Picture of variables on the stack

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

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

في Java، تذهب معظم الكائنات مباشرةً إلى الكومة.في لغات مثل C / C++، غالبًا ما تظل البنيات والفئات موجودة في المكدس عندما لا تتعامل مع المؤشرات.

ويمكن الاطلاع على مزيد من المعلومات هنا:

الفرق بين تخصيص الذاكرة المكدسة والكومة « timmurphy.org

و هنا:

إنشاء كائنات على المكدس والكومة

هذا المقال هو مصدر الصورة أعلاه: ستة مفاهيم مهمة لـ .NET:المكدس، والكومة، وأنواع القيم، وأنواع المراجع، والملاكمة، والفتح - CodeProject

ولكن انتبه أنه قد يحتوي على بعض الأخطاء.

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

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

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

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

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

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

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

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

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

ومع ذلك، فمن الأفضل عمومًا مراعاة "نِطَاق" و "حياة" بدلاً من "المكدس" و"الكومة".

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

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

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

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

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

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

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

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

لاحظ أن وضع الكلمة الأساسية "static" في الإعلان أعلاه يمنع var2 من أن يكون له نطاق عالمي.ومع ذلك، فإن var1 العالمي له تخصيص ثابت.هذا ليس بديهيا!لهذا السبب، أحاول عدم استخدام كلمة "ثابت" أبدًا عند وصف النطاق، وبدلاً من ذلك أقول شيئًا مثل نطاق "ملف" أو "ملف محدود".ومع ذلك، يستخدم العديد من الأشخاص عبارة "ثابت" أو "نطاق ثابت" لوصف متغير لا يمكن الوصول إليه إلا من خلال ملف تعليمات برمجية واحد.في سياق الحياة، "ثابت" دائماً يعني أن المتغير يتم تخصيصه عند بدء البرنامج وإلغاء تخصيصه عند خروج البرنامج.

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

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones

وقد أجاب آخرون على الخطوط العريضة بشكل جيد، لذلك سأقدم بعض التفاصيل.

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

  2. في لغة C، يمكنك الاستفادة من تخصيص الطول المتغير من خلال استخدام تخصيص, ، الذي يخصص على المكدس، على عكس التخصيص، الذي يخصص على الكومة.لن تنجو هذه الذاكرة من بيان الإرجاع الخاص بك، ولكنها مفيدة كمخزن مؤقت.

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

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

لقد أجاب الآخرون مباشرة على سؤالك، ولكن عند محاولة فهم المكدس والكومة، أعتقد أنه من المفيد النظر في تخطيط الذاكرة لعملية UNIX التقليدية (بدون سلاسل عمليات و mmap()المخصصون على أساس).ال معجم إدارة الذاكرة تحتوي صفحة الويب على رسم تخطيطي لتخطيط الذاكرة هذا.

يتم وضع المكدس والكومة بشكل تقليدي على طرفي نقيض من مساحة العنوان الافتراضية للعملية.تنمو المكدس تلقائيًا عند الوصول إليها، حتى الحجم الذي تحدده النواة (والذي يمكن تعديله باستخدام setrlimit(RLIMIT_STACK, ...)).تنمو الكومة عندما يستدعي مخصص الذاكرة brk() أو sbrk() استدعاء النظام، وتعيين المزيد من صفحات الذاكرة الفعلية في مساحة العنوان الافتراضية للعملية.

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

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

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

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

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

أعتقد أن العديد من الأشخاص الآخرين أعطوك إجابات صحيحة في الغالب حول هذا الموضوع.

ومع ذلك، هناك تفصيل واحد تم تفويته وهو أن "الكومة" ينبغي في الواقع أن تسمى "المتجر المجاني".سبب هذا التمييز هو أنه تم تنفيذ المتجر الحر الأصلي بهيكل بيانات يُعرف باسم "كومة ذات الحدين". لهذا السبب ، كان تخصيص التطبيقات المبكرة لـ Malloc ()/free () تخصيصًا من كومة.ومع ذلك، في هذا العصر الحديث، يتم تنفيذ معظم المتاجر المجانية باستخدام هياكل بيانات معقدة للغاية وليست أكوامًا ذات حدين.

ما هو المكدس؟

المكدس عبارة عن كومة من الكائنات، وعادة ما تكون مرتبة بشكل أنيق.

Enter image description here

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

ما هي الكومة؟

الكومة هي مجموعة غير مرتبة من الأشياء المتراكمة بشكل عشوائي.

Enter image description here

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

الاثنين معا

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

أيهما أسرع - المكدس أم الكومة؟و لماذا؟

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

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

نموذج ذاكرة جافا

Enter image description here

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

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

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

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

الكومة هي منطقة الذاكرة التي يتم إجراء عمليات تخصيص الذاكرة الديناميكية منها (استدعاءات "جديدة" أو "تخصيص" صريحة).إنها بنية بيانات خاصة يمكنها تتبع كتل الذاكرة ذات الأحجام المختلفة وحالة تخصيصها.

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

من ويكي أنوسر.

كومة

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

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

من المهم أخذ المكدس في الاعتبار عند معالجة الاستثناءات وتنفيذ سلسلة المحادثات.

كومة

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

كومة

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

كومة

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

نعم، ببساطة وبكلمات قصيرة، يقصدون أمر و غير مطلوب...!

كومة:في العناصر المكدسة، تصبح الأشياء فوق بعضها البعض، مما يعني أن معالجتها ستكون أسرع وأكثر كفاءة!...

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

كومة:لا يوجد ترتيب، وستكون المعالجة أبطأ ويتم العبث بالقيم معًا بدون ترتيب أو فهرس محدد...هناك عشوائية ولا توجد علاقة بينهما.لذلك يمكن أن يختلف وقت التنفيذ والاستخدام ...

أقوم أيضًا بإنشاء الصورة أدناه لإظهار كيف قد تبدو:

enter image description here

باختصار

يتم استخدام المكدس لتخصيص الذاكرة الثابتة والكومة لتخصيص الذاكرة الديناميكية، وكلاهما مخزن في ذاكرة الوصول العشوائي للكمبيوتر.


بالتفصيل

المدخنة

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

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

يمكن العثور على المزيد هنا.


الكومة

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

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

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

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

يمكن العثور على المزيد هنا.


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

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

Enter image description here

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

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

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

Enter image description here

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

وحتى يتم إعطاء المزيد من التفاصيل هنا و هنا.


الآن تعال إلى إجابات سؤالك.

إلى أي مدى يتم التحكم فيها بواسطة نظام التشغيل أو وقت تشغيل اللغة؟

يقوم نظام التشغيل بتخصيص المكدس لكل مؤشر ترابط على مستوى النظام عند إنشاء مؤشر الترابط.عادةً ما يتم استدعاء نظام التشغيل بواسطة وقت تشغيل اللغة لتخصيص الكومة للتطبيق.

يمكن العثور على المزيد هنا.

ما هو نطاقهم؟

أعطيت بالفعل في الأعلى.

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

يمكن العثور على المزيد في هنا.

ما الذي يحدد حجم كل منهم؟

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

ما الذي يجعل المرء أسرع؟

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

أيضا، المكدس مقابل.الكومة ليست مجرد اعتبار للأداء؛كما يخبرك كثيرًا عن العمر المتوقع للأشياء.

التفاصيل يمكن العثور عليها من هنا.

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

تم وضع برنامج C النموذجي مسطحًا في الذاكرة مع فرصة للزيادة عن طريق تغيير قيمة BRK ().عادةً ما كانت الكومة أقل بقليل من قيمة BRK وزيادة BRK زادت من كمية الكومة المتاحة.

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

كان أحد كتل الذاكرة النموذجية هو BSS (كتلة من قيم الصفر) والتي لم يتم صفرها بطريق الخطأ في عرض الشركة المصنعة.وكان الآخر هو DATA الذي يحتوي على قيم مهيأة، بما في ذلك السلاسل والأرقام.والثالث هو الكود الذي يحتوي على CRT (وقت تشغيل C) والوظائف الرئيسية والمكتبات.

أدى ظهور الذاكرة الافتراضية في UNIX إلى تغيير العديد من القيود.لا يوجد سبب موضوعي لعلاج هذه الكتل تكون متجاورة أو ثابتة في الحجم ، أو تم طلبها بطريقة معينة الآن.بالطبع، قبل UNIX كان هناك Multics التي لم تكن تعاني من هذه القيود.فيما يلي رسم تخطيطي يوضح أحد تخطيطات الذاكرة في تلك الحقبة.

A typical 1980s style UNIX C program memory layout

كومة, كومة و بيانات لكل عملية في الذاكرة الافتراضية:

stack, heap and static data

بضعة سنتات:أعتقد أنه سيكون من الجيد رسم الذاكرة بشكل رسومي وأكثر بساطة:

This is my vision of process memory construction with simplification for more easy understanding wht happening


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

طريقة بسيطة جدا:تعتبر كومة العملية عامة للعملية وجميع سلاسل العمليات بداخلها، وتستخدم لتخصيص الذاكرة في الحالة الشائعة مع شيء من هذا القبيل مالوك ().

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

نظرًا لأن بعض الإجابات قد تم تدقيقها، فسوف أساهم في رسالتي.

والمثير للدهشة أنه لم يذكر أحد هذا التعدد (أي.لا تتعلق بعدد سلاسل العمليات قيد التشغيل على مستوى نظام التشغيل) يمكن العثور على مكدسات الاستدعاءات ليس فقط في اللغات الغريبة (PostScript) أو الأنظمة الأساسية (Intel Itanium)، ولكن أيضًا في ألياف, المواضيع الخضراء وبعض تطبيقات كوروتين.

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

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

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

لاحظ أنني قلت "عادة لديك مكدس منفصل لكل وظيفة".هناك كلاهما مكدس و غير مكدس تطبيقات coroutines.أبرز تطبيقات C++ المكدسة هي Boost.Coroutine و مايكروسوفت بي بي إلasync/await.(ومع ذلك، C++'s وظائف قابلة للاستئناف (الملقب ب."async و await")، والتي تم اقتراحها على C++ 17، من المرجح أن تستخدم coroutines مكدسة.)

اقتراح الألياف لمكتبة C++ القياسية وشيك.أيضًا، هناك بعض الجهات الخارجية المكتبات.تحظى الخيوط الخضراء بشعبية كبيرة في لغات مثل بايثون وروبي.

لدي شيء لأشاركه، على الرغم من أن النقاط الرئيسية قد تمت تغطيتها بالفعل.

كومة

  • وصول سريع جدا.
  • مخزنة في ذاكرة الوصول العشوائي.
  • يتم تحميل استدعاءات الوظائف هنا مع المتغيرات المحلية ومعلمات الوظيفة التي تم تمريرها.
  • يتم تحرير المساحة تلقائيًا عندما يخرج البرنامج عن النطاق.
  • مخزنة في الذاكرة التسلسلية.

كومة

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

ملاحظة مثيرة للاهتمام:

  • إذا تم تخزين استدعاءات الوظائف في الكومة، فقد يؤدي ذلك إلى نقطتين فوضويتين:
    1. نظرًا للتخزين المتسلسل في المكدس، يكون التنفيذ أسرع.كان من الممكن أن يؤدي التخزين في الكومة إلى استهلاك وقت كبير مما يجعل تنفيذ البرنامج بأكمله أبطأ.
    2. إذا تم تخزين الوظائف في الكومة (التخزين الفوضوي المشار إليه بالمؤشر)، فلن تكون هناك طريقة للعودة إلى عنوان المتصل مرة أخرى (وهو ما توفره المكدس بسبب التخزين المتسلسل في الذاكرة).

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

على المكدس، يمكنك حفظ عناوين الإرجاع والاتصال → الضغط / إعادة → تتم إدارة البوب ​​مباشرة في الأجهزة.

يمكنك استخدام المكدس لتمرير المعلمات.حتى لو كان أبطأ من استخدام السجلات (هل سيقول أحد خبراء المعالجات الدقيقة أو كتاب BIOS جيد في الثمانينيات ...)

  • بدون كومة لا يمكن أن تعمل المعالجات الدقيقة.(لا يمكننا تخيل برنامج، حتى في لغة التجميع، بدون إجراءات فرعية/وظائف)
  • دون الكومة ما في وسعها.(يمكن أن يعمل برنامج لغة التجميع بدونه، نظرًا لأن الكومة هي مفهوم نظام التشغيل، كما أن malloc هو استدعاء OS/Lib.

استخدام المكدس أسرع كما يلي:

  • تعتبر الأجهزة، وحتى الدفع/البوب ​​فعالة للغاية.
  • يتطلب malloc الدخول في وضع kernel، واستخدام القفل/الإشارة (أو غيرها من أساسيات المزامنة) لتنفيذ بعض التعليمات البرمجية وإدارة بعض الهياكل اللازمة لتتبع التخصيص.

رائع!الإجابات كثيرة ولا أعتقد أن أياً منها قد نجح في الإجابة عليها...

1) أين وما هي (فعلياً في ذاكرة الكمبيوتر الحقيقية)؟

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

هناك نوعان من الأكوام:عام و شخصي.

تبدأ الكومة الخاصة عند حد 16 بايت (لبرامج 64 بت) أو حد 8 بايت (لبرامج 32 بت) بعد البايت الأخير من التعليمات البرمجية في برنامجك، ثم تزداد القيمة من هناك.ويسمى أيضًا الكومة الافتراضية.

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

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

2) إلى أي مدى يتم التحكم فيها بواسطة نظام التشغيل أو وقت تشغيل اللغة؟

يتم التحكم في المكدس بواسطة المبرمج، ويتم إدارة الكومة الخاصة بواسطة نظام التشغيل، ولا يتحكم أي شخص في الكومة العامة لأنها خدمة نظام التشغيل - أنت تقدم طلبات ويتم منحها أو رفضها.

2ب) ما هو نطاقها؟

جميعها عالمية بالنسبة للبرنامج، لكن محتوياتها يمكن أن تكون خاصة أو عامة أو عالمية.

2ج) ما الذي يحدد حجم كل منهما؟

يتم تحديد حجم المكدس والكومة الخاصة من خلال خيارات وقت تشغيل برنامج التحويل البرمجي لديك.تتم تهيئة الكومة العامة في وقت التشغيل باستخدام معلمة الحجم.

2د) ما الذي يجعل الشخص أسرع؟

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

المرجع:

https://norasandler.com/2019/02/18/Write-a-Compiler-10.html

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top