عشان تكتب كود C# نضيف وشغال تمام، لازم تفهم الـ Memory بتشتغل إزاي. المقالة دي هتوضحلك المفاهيم الأساسية زي الـ Stack، الـ Heap، الـ Value Types، الـ Reference Types، والـ Garbage Collector.
(لو محتاج، ممكن ترجع تبص على الكلام اللي قلناه قبل كده عن CLS and CTS in .NET Framework).

الـ Memory في أي برنامج في The .NET Platform متقسمة منطقتين أساسيتين بنخزن فيهم المتغيرات بتاعتنا:
- الـ Stack
- الـ Heap
The Stack
-
الوصف:
- مساحة محدودة وثابتة: الـ Stack حجمه ثابت. ده بيخليه سريع جدًا في إنه يحط أو يمسح الـ data.
- التخزين: بيستخدم بشكل أساسي عشان يخزن الـ Value Types وعناوين الـ Reference Types (الـ data بتاعتها الأصلية بتكون متخزنة في الـ Heap).
- الـ Overflow: لو الـ Stack اتملى زيادة عن حجمه المسموح، بتحصل غلطة مشهورة اسمها Stack Overflow. عشان كده لازم تخلي بالك وأنت بتستخدمه (بالذات لو بتستخدم الـ Recursion).
-
الخصائص:
- الوصول السريع: طريقة شغله هي LIFO (Last-In, First-Out)، يعني آخر حاجة بتدخل هي أول حاجة بتطلع. ده بيخلي الوصول للـ data اللي عليه سريع جدًا.
- عمر المتغير مرتبط بالـ Scope: الـ data اللي بتتحط على الـ Stack بتفضل موجودة طول ما الـ scope (المكان) اللي هي متعرفة فيه لسه شغال. أول ما الـ scope ده يخلص، الـ variables دي بتتشال تلقائي من الـ Stack.
-
مثال:
void ExampleMethod()
{
int a = 10; // 'a' is Value Type, stored directly on the Stack
int b = a; // 'b' is another Value Type, a copy of 'a' is stored on the Stack
// When ExampleMethod finishes, 'a' and 'b' are automatically removed from the Stack
}The Heap
- الوصف:
- مساحة كبيرة وبتتغير: الـ Heap مساحته أكبر بكتير من الـ Stack وممكن تزيد أو تقل حسب ما البرنامج يحتاج. ده بيخليه مناسب أكتر لتخزين الـ Reference Types والـ Objects اللي أحجامها كبيرة.
- التخزين: بيخزن الـ Objects اللي بنعملها لما نستخدم كلمة new Keyword. الـ data الحقيقية بتاعت الـ Object بتكون موجودة في الـ Heap، لكن الـ reference (اللي هو العنوان بتاعها في الـ Heap) هو اللي بيتخزن على الـ Stack في المتغير بتاعك.
- الخصائص:
- الـ Garbage Collector (GC) هو المسؤول: عملية حجز الـ memory ومسحها في الـ Heap بتحصل بشكل أوتوماتيكي عن طريق الـ Garbage Collector.
- أبطأ في الوصول: الوصول للـ data اللي على الـ Heap يعتبر أبطأ شوية مقارنة بالـ Stack. ده بيرجع لطريقة إدارته وإنك بتحتاج تروح للـ address (العنوان) الأول وبعدين توصل للـ data.
- مثال:
void ExampleMethod()
{
// 'obj' is a variable on the Stack, holding a reference (memory address)
MyClass obj = new MyClass();
// The actual MyClass object's data is allocated and stored on the Heap
// When ExampleMethod finishes, 'obj' (the reference on the Stack) is removed.
// The object on the Heap *might* become eligible for Garbage Collection if no other references point to it.
}
class MyClass { /* ... fields and methods ... */ }Value Types
- التعريف:
- بتخزن الـ Value مباشرةً: الـ Value Types بتخزن الـ Value بتاعتها في نفس المكان اللي متعرفة فيه في الـ Memory.
- بيتحجز ليها مكان في الـ Memory أول ما تديلها قيمة فعلية. لو عرفتها بس من غير ما تحط فيها قيمة، مش بيتحجز مكان للـ data لسه.
- أمثلة: الأرقام بكل أنواعها (
int,float,double,bool)، الـ Struct، والـ Enum.
- الخصائص:
- مكان التخزين: بتتخزن على الـ Stack لو هي variables محلية (local variables) جوه Method معينة. لو هي جزء من Object (يعني field جوه Class)، ساعتها بتتخزن جوه الـ Object ده على الـ Heap.
- مفيش وراثة: الـ
structوالـenumمينفعش يورثوا من أي حاجة تانية. - الـ
null: الـ Value Types مينفعش تاخد قيمةnullبشكل مباشر. لو احتجت إنها تقبلnull، بتستخدم حاجة اسمها Nullable Type (زي ما بتكتبint?بدلint). ممكن تشوف تفاصيل أكتر هنا: Cs Null.
- مثال:
// int is a C# keyword for System.Int32
int x; // Allocation of 4 uninitialized Bytes on the Stack for x
x = 5; // The value 5 is placed directly into the allocated bytes for x
// Int32 is the actual BCL (Base Class Library) type (seen in IL code)
Int32 y; // Allocation of 4 uninitialized Bytes on the Stack for y
y = x; // The *value* of x (which is 5) is copied into the location of y.
// Now x is 5 and y is 5, but they are independent copies.
++x; // Increment the value of x by 1.
// Now: x becomes 6, but y remains 5 because y holds its own copy.
Console.WriteLine($"x = {x}"); // Output: x = 6
Console.WriteLine($"y = {y}"); // Output: y = 5
- الوصول لـ Value Type قبل ما تديله قيمة:
int x; // Declared but not assigned a value
// Trying to use 'x' before assigning it will cause a compile-time error
// Console.WriteLine(x); // Error: Use of unassigned local variable 'x'
// y = x; // Error: Use of unassigned local variable 'x'Reference Types
- التعريف:
- لما بتعرف Variable من نوع Reference Type (زي
Object o1;)، ده مش بيحجز مكان للـ data نفسها في الـ Memory في اللحظة دي. الـ Variable ده بيكون مجرد مكان فاضي على الـ Stack مستني يتحط فيه الـ reference (العنوان) بتاع الـ Object. - عشان تحجز مكان للـ data وتعمل الـ Object نفسه، لازم تستخدم كلمة new Keyword.
- لما بتستخدم new Keyword، بيتحجز مكان للـ Object ده في الـ Heap وبيتحط فيه قيم مبدئية (initial values) حسب نوع الـ data أو عن طريق الـ Constructors in CSharp. (مثال: لما بتعمل Array جديدة زي هنا: Create Array).
- التخزين غير مباشر: الـ Variable بتاعك (اللي هو على الـ Stack) بيخزن عنوان (reference) بيشاور على المكان اللي فيه الـ data الحقيقية بتاعة الـ Object ده في الـ Heap.
- أمثلة: الـ Class، الـ String، الـ Array، والـ Delegate. (الـ
stringيعتبر Reference Type لأن حجمه مش ثابت وممكن يكون كبير جدًا).
- لما بتعرف Variable من نوع Reference Type (زي
- الخصائص:
- مثال:
Object o1; // Declared on the Stack, currently holds 'null'. Zero bytes allocated on the Heap *for this specific object* yet.
// Using 'new' (newobj in IL) allocates memory on the Heap for a new Object,
// initializes it, runs its constructor (if any), and returns the memory address (reference).
o1 = new Object(); // Now 'o1' on the Stack holds the reference to the new Object on the Heap.
Object o2 = new Object(); // 'o2' on the Stack holds a reference to a *second*, different Object on the Heap.
o2 = o1; // CRITICAL: Now, the reference stored in 'o1' is copied into 'o2'.
// Both 'o1' and 'o2' now point to the *same original object* that 'o1' was pointing to.
// The second object (that 'o2' initially pointed to) no longer has any references pointing to it
// and becomes eligible for Garbage Collection.- ملاحظات:
- لما تخلي Variable من نوع reference يشاور على نفس الـ Object اللي بيشاور عليه Variable تاني (
o2 = o1;)، الاتنين كده بقوا بيشاوروا على نفس الـ Object بالظبط في الـ Heap. أي تغيير هتعمله عن طريقo1هيظهر لو بصيت من خلالo2، والعكس صحيح. - لو Object معين في الـ Heap مبقاش فيه أي Variable بيشاور عليه، الـ Object ده بنقول عليه unreachable (يعني مش قادرين نوصله بأي شكل). في الحالة دي، الـ Garbage Collector (GC) ممكن يمسحه عشان يفضي المكان بتاعه.
- الـ reference في C# عامل زي الـ Pointer في C++ في فكرة إنه بيشاور على مكان في الـ Memory، لكن طريقة استخدامه والتعامل معاه في C# أبسط وأأمن بكتير.
- لما تخلي Variable من نوع reference يشاور على نفس الـ Object اللي بيشاور عليه Variable تاني (

The new Keyword
لما بتستخدم كلمة new Keyword عشان تعمل Object من Reference Type، شوية خطوات بتحصل ورا الستار:
- حجز الذاكرة (Allocation):
- الـ CLR بيحسب حجم الـ Object المطلوب وبيحجزله مكان مناسب في الـ Heap. الحجم ده بيكون حجم الـ data اللي جوه الـ Object + مساحة إضافية الـ CLR بيستخدمها عشان يدير الـ Object ده (اسمها CLR Overhead).
- ملحوظة عن الـ Overhead: أي حاجة بتتحط في الـ Heap، الـ CLR غالبًا بيضيف معاها معلومتين زيادة (زي الـ Sync Block Index والـ Type Object Pointer)، كل واحدة بتاخد حوالي 8 بايت (على نظام 64-bit)، يعني بيكون فيه تقريبًا 16 بايت زيادة بتتضاف على حجم الـ data الأصلية بتاعت كل Object في الـ Heap.
- تهيئة الذاكرة (Initialization):
- المكان اللي اتحجز ده بيتم “تصفيره” أو إنه يتحط فيه القيم الافتراضية لكل الـ fields اللي جوه الـ Object ده (الأرقام بتبقى 0، الـ booleans بتبقى
false، والـ references بتبقىnull).
- المكان اللي اتحجز ده بيتم “تصفيره” أو إنه يتحط فيه القيم الافتراضية لكل الـ fields اللي جوه الـ Object ده (الأرقام بتبقى 0، الـ booleans بتبقى
- استدعاء الـ Constructor:
- لو الـ Class ده فيه Constructor معين أنت كاتبه، الـ CLR بيناديه عشان يدي قيم أولية معينة للـ Object أو يعمل أي logic ضروري في الأول.
- تعيين الـ Reference:
- الـ CLR بيرجّع العنوان بتاع الـ Object الجديد اللي اتعمل في الـ Heap، والعنوان ده بيتخزن في الـ Variable بتاعك اللي موجود على الـ Stack.
- مثال:
// 'obj' is the variable on the Stack
// 'new MyClass()' triggers the 4 steps above:
// 1. Allocate memory on Heap for a MyClass object + overhead.
// 2. Initialize fields to defaults (e.g., 0, null).
// 3. Call the MyClass constructor (if defined).
// 4. Store the returned Heap address (reference) in the 'obj' variable on the Stack.
MyClass obj = new MyClass();The Garbage Collector (GC)
-
بيشتغل إمتى؟ لو الـ Heap قرب يتملي أو وصل لدرجة معينة من الضغط على الـ Memory، الـ CLR بيلاحظ ده فبينادي الـ Garbage Collector عشان يقوم بشغله وينضف الـ Memory.
-
الوظيفة:
- إدارة الـ Memory تلقائي: الـ GC هو المسؤول عن عملية تنضيف الـ Memory في الـ Heap لوحده. هو اللي بيمسح (free) الـ Objects اللي البرنامج مبقاش محتاجها أو مش قادر يوصلها.
- الـ Objects اللي مش بنوصلها (Unreachable): لما Object معين في الـ Heap مبقاش فيه أي Variable من نوع reference بيشاور عليه من أي حتة شغالة في البرنامج، الـ Object ده بيعتبر “garbage” (زبالة) وجاهز إن الـ GC يمسحه.
-
طريقة شغله:
- بيشتغل لوحده: الـ GC بيشتغل بشكل دوري وتلقائي حسب عوامل كتير زي حجم الـ Memory اللي بتستخدم وحالة الـ Heap. ممكن كمان تشغله يدويًا باستخدام
GC.Collect()، بس مش مستحب أبدًا تعمل كده في معظم الحالات لأنه ممكن يأثر على الأداء بشكل سلبي. - الـ Stop-the-World: وقت ما الـ GC بيشتغل عشان يجمع الـ garbage، ممكن يحتاج يوقف تنفيذ البرنامج بتاعك لوقت بسيط جدًا عشان يقدر يحدد بالظبط إيه الـ Objects اللي لسه مستخدمة وإيه اللي مبقتش مستخدمة ويمسحها بأمان. الوقفات البسيطة دي لو اتكررت كتير ممكن تحسس إن البرنامج بطيء أو بيقطع، عشان كده مهم من البداية تكتب كود بيستخدم الـ Memory بكفاءة.
- بيشتغل لوحده: الـ GC بيشتغل بشكل دوري وتلقائي حسب عوامل كتير زي حجم الـ Memory اللي بتستخدم وحالة الـ Heap. ممكن كمان تشغله يدويًا باستخدام
-
تأثيره على الأداء:
- قلل من الـ Allocation في الـ Heap: حاول متعملش Objects جديدة باستخدام new Keyword كتير من غير داعي، بالذات جوه loops، عشان تقلل عدد المرات اللي الـ GC بيحتاج يشتغل فيها.
- نضّف الـ Resources اللي الـ GC مش بيديرها: لو بتستخدم حاجات زي ملفات، اتصالات بقواعد بيانات، أو graphics، دي اسمها Unmanaged Resources. الـ GC مش بيمسح الـ Memory بتاعتها تلقائي. لازم أنت تمسحها بنفسك عن طريق إنك تستخدم الـ
IDisposableinterface و الـusingstatement. ممكن تشوف تفاصيل أكتر هنا: Cs IDisposable.
-
مهم تفتكر إن الـ Struct والـ Enum (Value Types) مش بيورثوا (مفيش Inheritance بينهم)، لكن الـ Class (Reference Type) بيدعم الوراثة (بس من Class أب واحد بس - Single Inheritance).
-
الفرق ده ممكن يكون نقطة مهمة وأنت بتقرر هتستخدم Struct ولا Class: لو عارف إن الحاجة دي ممكن تحتاج تورث من حاجة تانية أو حاجات تانية هتورث منها في المستقبل، غالبًا الحل هيكون Class. لو هي مجرد حاجة بسيطة بتشيل شوية data ومش محتاجة وراثة، الـ Struct ممكن يكون أفضل من ناحية الأداء في بعض السيناريوهات.
Summary
- الـ Stack: سريع، مساحته محدودة، بيخزن الـ Value Types وعناوين الـ Reference Types. بيشتغل بنظام LIFO. الـ Memory اللي فيه بتتمسح تلقائي لما الـ scope يخلص. ممكن يحصل فيه Stack Overflow.
- الـ Heap: مساحته كبيرة وممكن تتغير، بيخزن الـ Data الفعلية بتاعة الـ Reference Types. إدارته أبطأ شوية وبيتحكم فيه الـ Garbage Collector.
- الـ Value Types: بتشيل الـ Value مباشرة (زي
int,bool,struct). بتتخزن في الـ Stack (لو هي local variable) أو جوه الـ Object الأب اللي في الـ Heap. لما بتعمللها assignment بتتنقل بالـ Copy. مبتقبلش قيمةnull(إلا لو استخدمت Nullable Type). - الـ Reference Types: بتشيل الـ Address (عنوان) بيشاور على الـ Data اللي موجودة في الـ Heap (زي
class,string,array). الـ Data بتتخزن في الـ Heap والـ Address بيتخزن في الـ Stack. لما بتعمللها assignment بتتنقل بالـ Reference (كل المتغيرات بتشاور على نفس الـ Object). بتقبل قيمةnull. بتدعم الـ Inheritance. - الـ Garbage Collector: بينضف الـ Heap بشكل أوتوماتيكي وبيمسح الـ Objects اللي مبقاش فيه أي حاجة بتشاور عليها، وده عشان يوفر Memory ويحسن الأداء.
فهم الفروقات دي كويس هيساعدك تكتب كود C# أحسن، كفاءته أعلى، ومشاكله أقل بكتير بخصوص الـ Memory.