[Memory-management] ماذا وأين هي مكدس وكومة؟


Answers

كومة:

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

كومة:

  • تخزينها في ذاكرة الوصول العشوائي الكمبيوتر تماما مثل المكدس.
  • في C ++ ، يجب أن يتم إتلاف المتغيرات على الكومة يدوياً ولا تقع خارج نطاق. يتم تحرير البيانات مع delete أو delete[] أو free .
  • أبطأ لتخصيص بالمقارنة مع المتغيرات على المكدس.
  • تستخدم عند الطلب لتخصيص كتلة من البيانات لاستخدامها من قبل البرنامج.
  • يمكن أن يكون هناك تجزئة عندما يكون هناك الكثير من التوزيعات و deallocations.
  • في 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;
Question

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

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



Stack

  • Very fast access
  • Don't have to explicitly de-allocate variables
  • Space is managed efficiently by CPU, memory will not become fragmented
  • Local variables only
  • Limit on stack size (OS-dependent)
  • Variables cannot be resized

Heap

  • Variables can be accessed globally
  • No limit on memory size
  • (Relatively) slower access
  • No guaranteed efficient use of space, memory may become fragmented over time as blocks of memory are allocated, then freed
  • You must manage memory (you're in charge of allocating and freeing variables)
  • Variables can be resized using realloc()



I think many other people have given you mostly correct answers on this matter.

One detail that has been missed, however, is that the "heap" should in fact probably be called the "free store". The reason for this distinction is that the original free store was implemented with a data structure known as a "binomial heap." For that reason, allocating from early implementations of malloc()/free() was allocation from a heap. However, in this modern day, most free stores are implemented with very elaborate data structures that are not binomial heaps.




(لقد نقلت هذه الإجابة من سؤال آخر كان أكثر أو أقل من هذا السؤال.)

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

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

الكومة

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

المدخنة

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

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

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

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

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




I have something to share with you, although major points are already penned.

Stack

  • Very fast access.
  • Stored in RAM.
  • Function calls are loaded here along with the local variables and function parameters passed.
  • Space is freed automatically when program goes out of a scope.
  • Stored in sequential memory.

Heap

  • Slow access comparatively to Stack.
  • Stored in RAM.
  • Dynamically created variables are stored here, which later requires freeing the allocated memory after use.
  • Stored wherever memory allocation is done, accessed by pointer always.

Interesting note:

  • Should the function calls had been stored in heap, it would had resulted in 2 messy points:
    1. Due to sequential storage in stack, execution is faster. Storage in heap would have resulted in huge time consumption thus resulting whole program to execute slower.
    2. If functions were stored in heap (messy storage pointed by pointer), there would have been no way to return to the caller address back (which stack gives due to sequential storage in memory).

Feedbacks are wellcomed.




Others have answered the broad strokes pretty well, so I'll throw in a few details.

  1. Stack and heap need not be singular. A common situation in which you have more than one stack is if you have more than one thread in a process. In this case each thread has its own stack. You can also have more than one heap, for example some DLL configurations can result in different DLLs allocating from different heaps, which is why it's generally a bad idea to release memory allocated by a different library.

  2. In C you can get the benefit of variable length allocation through the use of alloca , which allocates on the stack, as opposed to alloc, which allocates on the heap. This memory won't survive your return statement, but it's useful for a scratch buffer.

  3. Making a huge temporary buffer on Windows that you don't use much of is not free. This is because the compiler will generate a stack probe loop that is called every time your function is entered to make sure the stack exists (because Windows uses a single guard page at the end of your stack to detect when it needs to grow the stack. If you access memory more than one page off the end of the stack you will crash). مثال:

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



What is a stack?

A stack is a pile of objects, typically one that is neatly arranged.

Stacks in computing architectures are regions of memory where data is added or removed in a last-in-first-out manner.
In a multi-threaded application, each thread will have its own stack.

What is a heap?

A heap is an untidy collection of things piled up haphazardly.

In computing architectures the heap is an area of dynamically-allocated memory that is managed automatically by the operating system or the memory manager library.
Memory on the heap is allocated, deallocated, and resized regularly during program execution, and this can lead to a problem called fragmentation.
Fragmentation occurs when memory objects are allocated with small spaces in between that are too small to hold additional memory objects.
The net result is a percentage of the heap space that is not usable for further memory allocations.

Both together

In a multi-threaded application, each thread will have its own stack. But, all the different threads will share the heap.
Because the different threads share the heap in a multi-threaded application, this also means that there has to be some coordination between the threads so that they don't try to access and manipulate the same piece(s) of memory in the heap at the same time.

Which is faster – the stack or the heap? و لماذا؟

The stack is much faster than the heap.
This is because of the way that memory is allocated on the stack.
Allocating memory on the stack is as simple as moving the stack pointer up.

For people new to programming, it's probably a good idea to use the stack since it's easier.
Because the stack is small, you would want to use it when you know exactly how much memory you will need for your data, or if you know the size of your data is very small.
It's better to use the heap when you know that you will need a lot of memory for your data, or you just are not sure how much memory you will need (like with a dynamic array).

Java Memory Model

The stack is the area of memory where local variables (including method parameters) are stored. When it comes to object variables, these are merely references (pointers) to the actual objects on the heap.
Every time an object is instantiated, a chunk of heap memory is set aside to hold the data (state) of that object. Since objects can contain other objects, some of this data can in fact hold references to those nested objects.




A couple of cents: I think, it will be good to draw memory graphical and more simple:


Arrows - show where grow stack and heap, process stack size have limit, defined in OS, thread stack size limits by parameters in thread create API usually. Heap usually limiting by process maximum virtual memory size, for 32 bit 2-4 GB for example.

So simple way: process heap is general for process and all threads inside, using for memory allocation in common case with something like malloc() .

Stack is quick memory for store in common case function return pointers and variables, processed as parameters in function call, local function variables.




  • المقدمة

Physical memory is the range of the physical addresses of the memory cells in which an application or system stores its data, code, and so on during execution. Memory management denotes the managing of these physical addresses by swapping the data from physical memory to a storage device and then back to physical memory when needed. The OS implements the memory management services using virtual memory. As a C# application developer you do not need to write any memory management services. The CLR uses the underlying OS memory management services to provide the memory model for C# or any other high-level language targeting the CLR.

Figure 4-1 shows physical memory that has been abstracted and managed by the OS, using the virtual memory concept. Virtual memory is the abstract view of the physical memory, managed by the OS. Virtual memory is simply a series of virtual addresses, and these virtual addresses are translated by the CPU into the physical address when needed.

Figure 4-1. CLR memory abstraction

The CLR provides the memory management abstract layer for the virtual execution environment, using the operating memory services. The abstracted concepts the CLR uses are AppDomain, thread, stack, heapmemorymapped file, and so on. The concept of the application domain (AppDomain) gives your application an isolated execution environment.

  • Memory Interaction between the CLR and OS

By looking at the stack trace while debugging the following C# application, using WinDbg, you will see how the CLR uses the underlying OS memory management services (eg, the HeapFree method from KERNEL32.dll, the RtlpFreeHeap method from ntdll.dll) to implement its own memory model:

using System;
namespace CH_04
{
    class Program
    {
        static void Main(string[] args)
        {
            Book book = new Book();
            Console.ReadLine();
        }
    }

    public class Book
    {
        public void Print() { Console.WriteLine(ToString()); }
    }
}

The compiled assembly of the program is loaded into WinDbg to start debugging. You use the following commands to initialize the debugging session:

0:000> sxe ld clrjit

0:000> g

0:000> .loadby sos clr

0:000> .load C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll

Then, you set a breakpoint at the Main method of the Program class, using the !bpmd command:

0:000>!bpmd CH_04.exe CH_04.Program.Main

To continue the execution and break at the breakpoint, execute the g command:

0:000> g

When the execution breaks at the breakpoint, you use the !eestack command to view the stack trace details of all threads running for the current process. The following output shows the stack trace for all the threads running for the application CH_04.exe:

0:000> !eestack

Thread 0

Current frame: (MethodDesc 00233800 +0 CH_04.Program.Main(System.String[]))

ChildEBP RetAddr Caller, Callee

0022ed24 5faf21db clr!CallDescrWorker+0x33

/ trace removed /

0022f218 77712d68 ntdll!RtlFreeHeap+0x142, calling ntdll!RtlpFreeHeap

0022f238 771df1ac KERNEL32!HeapFree+0x14, calling ntdll!RtlFreeHeap

0022f24c 5fb4c036 clr!EEHeapFree+0x36, calling KERNEL32!HeapFree

0022f260 5fb4c09d clr!EEHeapFreeInProcessHeap+0x24, calling clr!EEHeapFree

0022f274 5fb4c06d clr!operator delete[]+0x30, calling clr!EEHeapFreeInProcessHeap / trace removed /

0022f4d0 7771316f ntdll!RtlpFreeHeap+0xb7a, calling ntdll!_SEH_epilog4

0022f4d4 77712d68 ntdll!RtlFreeHeap+0x142, calling ntdll!RtlpFreeHeap

0022f4f4 771df1ac KERNEL32!HeapFree+0x14, calling ntdll!RtlFreeHeap

/ trace removed /

This stack trace indicates that the CLR uses OS memory management services to implement its own memory model. Any memory operation in.NET goes via the CLR memory layer to the OS memory management layer.

Figure 4-2 illustrates a typical C# application memory model used by the CLR at runtime.

Figure 4-2 . A typical C# application memory model

The CLR memory model is tightly coupled with the OS memory management services. To understand the CLR memory model, it is important to understand the underlying OS memory model. It is also crucial to know how the physical memory address space is abstracted into the virtual memory address space, the ways the virtual address space is being used by the user application and system application, how virtual-to-physical address mapping works, how memory-mapped file works, and so on. This background knowledge will improve your grasp of CLR memory model concepts, including AppDomain, stack, and heap.

For more information, refer to this book:

C# Deconstructed: Discover how C# works on the .NET Framework

This book + ClrViaC# + Windows Internals are excellent resources to known .net framework in depth and relation with OS.




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

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

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

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

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

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

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




OK, simply and in short words, they mean ordered and not ordered ...!

Stack : In stack items, things get on the top of each-other, means gonna be faster and more efficient to be processed!...

So there is always an index to point the specific item, also processing gonna be faster, there is relationship between the items as well!...

Heap : No order, processing gonna be slower and values are messed up together with no specific order or index... there are random and there is no relationship between them... so execution and usage time could be vary...

I also create the image below to show how they may look like:




Simply, the stack is where local variables get created. Also, every time you call a subroutine the program counter (pointer to the next machine instruction) and any important registers, and sometimes the parameters get pushed on the stack. Then any local variables inside the subroutine are pushed onto the stack (and used from there). When the subroutine finishes, that stuff all gets popped back off the stack. The PC and register data gets and put back where it was as it is popped, so your program can go on its merry way.

The heap is the area of memory dynamic memory allocations are made out of (explicit "new" or "allocate" calls). It is a special data structure that can keep track of blocks of memory of varying sizes and their allocation status.

In "classic" systems RAM was laid out such that the stack pointer started out at the bottom of memory, the heap pointer started out at the top, and they grew towards each other. If they overlap, you are out of RAM. That doesn't work with modern multi-threaded OSes though. Every thread has to have its own stack, and those can get created dynamicly.