We talked about CLS and CTS, Read it again

إدارة الذاكرة في C#

فهم إدارة الذاكرة في C# مهم جدًا عشان تكتب تطبيقات فعّالة ومستقرة. الجزء ده هيشرح المفاهيم الأساسية زي Stack، Heap، Value Types، Reference Types، وGarbage Collector.

نظرة عامة

الذاكرة في تطبيقات .NET مقسمة بشكل أساسي إلى منطقتين لتخزين المتغيرات:

  • Stack
  • Heap

Stack

  • الوصف:

    • مساحة محدودة وثابتة: الـ Stack عنده حجم محدود، وده بيخليه سريع في عمليات التخزين والإزالة.
    • التخزين: بيستخدم أساسًا لتخزين Value Types والعناوين بتاعة Reference Types اللي موجودة على الـ Heap.
    • التجاوز: لو الـ Stack اتعدى المساحة المخصصة ليه، هيطلع خطأ اسمه Stack Overflow، عشان كده لازم تبقى واخد بالك من استخدام الـ Stack.
  • الخصائص:

    • سرعة الوصول: بسبب طبيعتها اللي بتمشي من آخر حاجة دخلت أول حاجة تخرج (LIFO)، الوصول للبيانات على الـ Stack سريع جدًا.
    • مدة الحياة بناءً على النطاق: المتغيرات على الـ Stack بتتشال أوتوماتيكًا لما تخلص نطاقها.
  • مثال:

void ExampleMethod()
{
  int a = 10; // 'a' is Value Type (in Stack)
  int b = a;  // 'b' is Value Type (in Stack)
}

Heap

  • الوصف:
    • ديناميكي وكبير: الـ Heap عنده حجم أكبر وديناميكي، مناسب لتخزين Reference Types.
    • التخزين: بيخزن الكائنات اللي بتتعمل بكلمة new. البيانات الفعلية بتكون على الـ Heap، والعنوان بتاعها بيكون على الـ Stack.
  • الخصائص:
    • مدار من قبل الـ Garbage Collector: تخصيص وإزالة الذاكرة على الـ Heap بيتم أوتوماتيكًا بواسطة الـ Garbage Collector (GC).
    • سرعة أبطأ في الوصول: الوصول للبيانات على الـ Heap أبطأ مقارنة بالـ Stack بسبب طبيعتها الديناميكية.
  • مثال:
void ExampleMethod()
{
	MyClass obj = new MyClass(); 
	// Referenc for 'obj' in Stack, But the real object in Heap
}

Value Types

  • التعريف:
    • تخزن البيانات مباشرة: Value Types بتخزن بياناتها مباشرة في موقع الذاكرة اللي اتعرفت فيه. بتتخزن ويحجز مكان في الـ Memory لما بتديلها قيمة، انما لما بتعرفها بس مش بيتخزن البيانات لأن مفيش.
    • أمثلة: int, bool, double, Cs Struct, Cs Enums
  • الخصائص:
    • تخصيص الذاكرة: بتتخزن على الـ Stack لما تتعرف كمتغيرات محلية. لو كانت جزء من object، بتكون على الـ Heap مع object دا.
    • مفيش وراثة: struct و enum مبيتورثوش.
    • القيم الفارغة: مش بتقدر تاخد null إلا لو عملتها Nullable (زي int?) عن طريق الـ Cs Null.
  • مثال:
int X; // int: C# keyword 
// Allocation of 4 uninitialized Bytes in Stack
X = 5; // put 5 at the allocated bytes
 
Int32 Y; //Another way to define the integers
//Int32: BCL (IL code)
 
y = x; // At y location we put the value of X
++x; // x increased by 1 
// x = 6, y = 5

  • الوصول للـ Value Types غير المهيأة (Garbage):
int x;
Console.WriteLine(x);
// Error: Used uninitialized variable (x)
y = x;
// Error: Used uninitialized variable (x)

Reference Types

  • مش بيحجز مكان في ال memory، هو حاجة أشبه باسم الدلع او alias مش أكتر
  • لو عايز تحجزله مكان بقا بنستخدم كلمة new وبعدها هيبدأ يخزن الداتا فعلًا بس بيخزنها في ال Heap
  • أول ما بستخدم كلمة new بحجز مكان بالفعل وبيحطله initial value على حسب نوع الداتا أو من خلال الـ Cs Constructor مثال: لما باجي أعمل Array جديدة Create Array
  • التعريف:
    • تخزن بشكل غير مباشر: Reference Types بتخزن reference (عنوان) للبيانات الفعلية على الـ Heap.
    • أمثلة: Cs Class, Cs String, Cs Arrays, Cs Delegates ودا بسبب إن ال string ملوش مساحة محددة
  • الخصائص:
    • تخصيص الذاكرة: المرجع بيكون على الـ Stack، والبيانات الفعلية على الـ Heap.
    • الوراثة: بتدعم الوراثة، وبتسمح للـ Classes إنها ترث من Classes تانية.
    • القيم الفارغة: بتقدر تاخد null عشان تبين إن ما فيش كائن مشترك.
  • مثال:
Object o1; // zero bytes have been allocated in Heap
 
o1 = new Object();
// new(IL -> newObj)
 
Object o2 = new Object();
o2 = o1;
  • ملاحظات:
    • لما تربط مرجع بمرجع تاني، الاتنين بيشيروا لنفس الكائن على الـ Heap.
    • لو مافيش أي مراجع بتشير لكائن على الـ Heap، يبقى الكائن ده مؤهل ليتم جمعه بواسطة الـ Garbage Collector.
    • ملحوظة: ال reference عامل زي ال pointer بس بشكل كتابة مختلف وبتعمل نفس الوظائف

كلمة new

لما تستخدم كلمة new مع Reference Type، الحاجات دي بتحصل:

  1. تخصيص الذاكرة:
    • بتخصص عدد البايتات المطلوبة على الـ Heap للكائن (حجم الكائن + الـ CLR Overhead).
    • مثال: new MyClass() بيخصص مساحة لكائن جديد من نوع MyClass.
    • أي حاجة بتحطها في ال Heap بيبدأ ال CLR بيحط اتنين variables عشان يستخدمهم
    • بيحط 64 bit لكل variable
    • فكدا بيبقا فيه 16 byte زيادة على أي حاجة بتتخزن في ال heap
  2. تهيئة الذاكرة:
    • بتحط القيم الافتراضية للنوع في الذاكرة المخصصة.
  3. استدعاء الكونستركتور:
    • لو في كونستركتور معرف من قبل، بيتنادى عشان يهيئ الكائن.
  4. تعيين المرجع:
    • المرجع (عنوان الكائن على الـ Heap) بيتخصص للمتغير.
  • مثال:
MyClass obj = new MyClass(); 
// 'obj' save a reference for new object from MyClass in Heap

Garbage Collector

  • لو ال heap قرب يتملى بيبدأ ال CLR يلاحظ الحوار دا فبينادي على الgarbage collector

  • الوظيفة:

    • إدارة الذاكرة أوتوماتيكًا: الـ Garbage Collector (GC) بيحرر (free) الذاكرة المشغولة بالـ Objects اللي مش متوصلة بالـ Application.
    • الكائنات غير القابلة للوصول: لما مافيش references بتشير لـ Object على الـ Heap، الـ Object بيبقى unreachable وبيبقى garbage.
  • التشغيل:

    • تشغيل الـ GC: الـ GC بيشتغل بشكل دوري بناءً على استخدام الذاكرة ومعايير تانية. ممكن كمان تناديه يدويًا باستخدام GC.Collect(), لكن مش مستحب.
    • الـStop-the-World: أثناء جمع الـ Garbage، التطبيق ممكن يوقف لفترة قصيرة عشان الـ GC يقدر يحرر الذاكرة. التوقفات المتكررة دي ممكن تأثر على الأداء، فالأفضل إدارة الذاكرة بشكل كويس.
  • تأثير على الأداء:

    • تقليل تخصيصات الـ Heap: قلل من التخصيصات غير الضرورية عشان تقلل من تكرار جمع الـ Garbage.
    • تحرير الموارد غير المُدارة: استخدم IDisposable و using statements لتحرير الموارد غير المُدارة بسرعة.
  • افتكر اني قولت فوق على ال Struct, Enum انهم مش بيورثوا انما ال class بيورث لـ single parent بس فانت اعمل حسابك لو حاجة هتورث قدام تستخدم ال class (reference) لو غير كدا تستخدم الحاجات التانية

الخلاصة

  • الـStack: سريع، مساحة محدودة لتخزين Value Types والمرجعيات لـ Reference Types. عرضة لـ Stack Overflow لو اتعدت.
  • الـHeap: مساحة أكبر وديناميكية لتخزين Reference Types. مدار من قبل الـ Garbage Collector اللي بيحرر الذاكرة للكائنات اللي مش مستخدمة.
  • الـValue Types: بتخزن البيانات مباشرة، على الـ Stack أو جوا Objects على الـ Heap. مش بتقدر تاخد null إلا لو عملتها Nullable.
  • الـReference Types: بتخزن مرجع للبيانات على الـ Heap، بتدعم الوراثة، وبتقدر تاخد null.
  • الـGarbage Collector: بيحكم إدارة الذاكرة على الـ Heap أوتوماتيكًا، وبيحرر الكائنات اللي مش مستخدمة لتحسين استخدام الذاكرة وأداء التطبيق.

فهم المفاهيم دي مهم جدًا لإدارة الذاكرة بكفاءة في C#، وده بيؤدي لتطبيقات محكمة وموثوقة.