مستوى المقال: متوسط — يفترض إنك كاتبت دوال وعرّفت متغيرات قبل كده، وعندك فكرة بسيطة عن stack frame أو pointer. مش لازم تكون بتكتب C، الأمثلة بـ Go لكن المبدأ نفسه في Java و C# و JS و Rust.
لو دالة بتعمل allocate لـ struct صغيرة في 0.4 نانوثانية، ودالة تانية بتعمل نفس الـ struct في 24 نانوثانية، الفرق مش الـ CPU. الفرق إن واحدة بتشتغل على Stack والثانية بتروح Heap. هنا بالظبط إيه الفرق، وامتى تختار كل واحد فيهم.
Stack vs Heap: التقسيم اللي البرنامج بتاعك بيشتغل عليه فعلاً
المشكلة باختصار
كل برنامج شغّال بياخد block من الذاكرة وقت ما الـ OS بيحمّله. الـ block ده مش قطعة واحدة، هو متقسم لمناطق. أهم منطقتين بتأثر على سرعة الكود وثباته هما Stack و Heap. أغلب الـ bugs اللي شكلها غريب — segmentation fault، stack overflow، memory leak، GC pause طويلة — مصدرها سوء فهم لمين بيقعد فين.
المثال اللي هيخلي الفكرة تثبت
تخيّل عندك مطعم. الـ Stack هو الطبق اللي قدامك على الترابيزة: المكان محدود، بتحط عليه أكلك دلوقتي وبس، وأول ما تخلص بترفع الطبق فورًا بدون تفكير. كل حاجة تحطها بترجع تشيلها بالترتيب العكسي: آخر طبق نزل أول طبق يطلع. ده بالظبط معنى LIFO.
الـ Heap هو ثلاجة المطعم: مساحتها أكبر بكتير، ممكن تحط فيها أي حاجة لأي مدة، بس كل مرة تحتاج تفتحها وتدوّر على الطبق المخصوص ده عملية أبطأ. كمان لازم حد ينضّف الثلاجة كل فترة وإلا هتمتلي بأكل قديم — ده الـ Garbage Collector في Java/Go/JS، أو الـ free اليدوي في C.
الكود بتاعك بيشتغل بنفس المنطق. المتغيرات الصغيرة قصيرة العمر بتتحط على Stack. البيانات الكبيرة أو اللي عمرها بيمتد بعد ما الدالة تخلص بتروح Heap.
التعريف العلمي بدون لف
الـ Stack منطقة ذاكرة بتشتغل بنظام LIFO. لكل دالة بتتنادى، الـ runtime بيحجز stack frame فيه المتغيرات المحلية والـ return address والـ saved registers. بمجرد ما الدالة ترجع، الـ frame كله بيتحذف بتعديل register واحد اسمه stack pointer. الـ allocation هنا O(1) فعلي، بدون أي بحث، وبدون أي metadata.
الـ Heap منطقة بتُدار بـ allocator (malloc في C، new في C++/Java، runtime في Go، V8 في JS). كل allocation بيتطلب من الـ allocator يدوّر على free block بحجم كافي، يقسّمه، يحدّث metadata، ويرجّع pointer. ده بياخد وقت أطول، وأي free لازم يحصل صراحةً (في C/C++) أو عن طريق Garbage Collector (في Java/Go/JS).
مثال كود تقدر تجرّبه دلوقتي
الكود ده في Go بيقيس الفرق على نفس البيانات بالظبط. الفرق الوحيد إن دالة بترجّع value (Stack) والثانية بترجّع pointer (Heap):