بسم الله الرحمن الرحيم
السلام عليكم
إنهاردة بأذن الله هنتعلم بعض الاساسيات الهامة التى تمكنا من فهم الGarbage collection وهى
كيفية إنشاء كائن داخل الManaged heap
ما هو الApplication root وماهى أهميتة للكائن؟
العلاقة بين الgenerations and objects
Finalization and IDisposable interface
بعض الداول الموجودة داخل الGarbage collector class
عملية ال Resurrection (الإحياء)
Weak reference and strong reference
الجزء الاول
مقـــــــــدمة بسيطة:-
كلنا عارفين أزاى أنشى values and objects وعارفين أن الreference types تخزن داخل الManaged heap ( Managed heap is just a random chuck of memory that accessed by CLR ) زى الclasses وأيضا عارفين أن الvalue types بتخزن حسب المكان المعرفة فية بمعنى لو كانت معرف داخل class يبقى هتتخزن ف الManaged heap وأيضا لو كانت معرفة داخل structure يبقى هتخزن داخل الstack . حيث ان عملية الreclaim للذاكرة لو كانت مقتصرة على المبرمج سوف تكون صعبة جدا وممكن تبعدة عن المشلكة الاساسية التى يحاول حلها وبالتالى الGarbage collector سهل العملية على المبرمج من تتبع أستعمال الذاكرة ومتى يحرر الMemory ولكن فى بعض الأوقات كال unmanaged resources (كما سنعرف ) الGC لا يعرف كيفية تحرير الMemory وبالتالى فهذ يجبر المبرمج على كتابة كود بسيط يعرف من خلالة الGC أزاى يتعامل مع الunmanaged resources حيث فى الدوت نت يتم كتابة الDispose , Finalize or Close methods (كما سنرى) طب دلوقتى عاوزين نعرف أزاى يتم عمل Clean للunreachable resources الموجودة فى الmanaged heap وأزاى أقدر أتحكم فى الGC .
How objects store in Managed Heap?
تعالى نعرف أزاى يتم عمل Initialize and allocate to object
على أفتراض أن عندنا Employee class كما موضح بالكود
class Employee
{
private string m_name;
private int m_age;
public Employee(string name , int age)
{
this.m_name = name;
this.m_age = age;
}
public void PrintData()
{
Console.WriteLine("the name is {0} and age is {1}",this.m_name,this.m_age);
}
}
class Program
{
static void Main(string[] args)
{
Employee emp = new Employee("Mohammed Allam", 19);
emp.PrintData();
}
}فى ذلك السطر
Employee emp = new Employee("Mohammed Allam", 19);تم أستخدام الnew keyword حيث بترجع reference للobject(emp) الموجود داخل الManaged heap وليس الكائن ذات نفسة كما موضح ف تلك الصورة
طب فى حالة عمل running للبرنامج وعند فتح الAssembly بأستخدام الildasm.exe سنجد الnew keyword متمثلة فى الnewobj instruction كما موضح فى تلك الصورة
تعالى نعرف المهام اللى بيقوم بيها الinstruction newobj
1-يتم حساب المساحة الكلية المراد حجزها لذلك المتغير وذلك عن طريق data types للمتغيرات الموجودة داخل ذلك الclass وأيضا الdata typesللbase class
2-وبعد ذلك يتم فحص الManaged heap هل عندك مساحة كافية لتخزين ذلك الobject لو فية مساحة فى تلك الحالة يتم مناداة الcustom constructor وبعد ذلك يتم إرجاع reference لذلك الobject حيث المساحة أو المكان اللى تم تخزين فية ذلك الobject كان مشار الية بمؤشر يسمى Next object pointer (NextObjPtr حيث عبارة عن المكان اللى سيتم تخزين فية الNext object داخل الManaged heap ) كما موضح فى تلك الصورة
حيث فى تلك الصورة عندنا object a,b,c موجودين داخل الmanaged heap والNextObjPtr يشير الى المكان المتاح لاى object جديد سيتم تخزينة
حيث عند أنشاء object يتم تحديد المساحة كما عرفنا سابقا ويتم إضافة تلك المساحة المحددة الى الNextObjPtr
أذا كان ال NextObjPtr وصل الى نهاية المساحة المحددة للManaged heap وبالتالى يبدأ الGC لعمل clean كما سنعرف حتى يتيح تخزين ذلك الobject
Theory of Application Root
أى APP. لة مجموعة من ال roots حيث الroot عبارة عن مساحة مخزنة تشير الى الobject الموجود داخل ال heap
حيث الroot ممكن أن يكون من أحدى الحالات الأتية
1-Global or static object
EX: public static Employee emp;
2-Object parameter passed to method
Public void PrintData(Employee emp)
{
//…..
}3-ٍStatic Filed
EX: public static int count = 9;
4- Local object declared in method
Public void Add()
{
Employee emp = new Employee("A",1);
}حيث تلك الroots تحدد عن طريق الـJIT(Just In Time compiler) وأيضا الCLR
عند بداية تشغيل الGC (Garbage collector ) يعتبر جميع الobjects المخزنة داخل الheap عبارة عن Garbage بمعنى أن No Application roots refer to any object in heap وبكدة يبدأ عمل الGC وبيدأ يختبر هل كل object موجود متصل بالroot بتاعة ولا لأ وعلى هذا الاساس يتم بناء الobject graph بواسطة الCLR وأيضا بيكون حريص فى الcircular objects حيث عمرها ماهتكون infinite
على إفتراض ان عندنا objects كما موجود فى تلك الصورة
حيث جميع الobjects الموجودة جزء من الobject graph وعند أضافة الobject D حيث لاحظ المجمع (Collector ) أنة معتمد على الobject h والobjh موجود ف الgraph
وهكذا الGC يبدأ يختبر كل obj هل لة root ؟لو لة يضيفة فى الgraph طب لو لو ملهوش وبالتالى يكون unreachable object وبالتالى يبدأ الGC أيضا فى إختبار هل يوجد unreachable objects عبارة عن finalizable (كما سنعرف) وبالتالى يتم وضع مؤشر لة داخل الfreachable queue ليتم مناداة الfinalize لتحرير الذاكرة من ذلك الكائن
وبعد ذلك يتم عمل deallocate للunreachable objects اللى مش محتاجة الfinalization process وذلك عن طريق
تحريك الreachable object لاسفل فى الheap وبعد ذلك يبدأ فى عمل Defragment للheap وبالتالى بسبب التحريك الreachable objects يتم عمل update للApplication roots التى تشير الى objects فى المكان الجديد وأيضا الNextObjPtr يير الى المساحة الفارغة
حيث فى تلك اللحظة تم إقاف باقى الthreads حتى يتم عملية الclean وبعد ذلك تبدأ باقى الthreads فى عملها وأيضا يتم إنشاء thread أخرى منفصلة للunreachable objects اللى محتاج عمل finalize لها قبل حذفها من الذاكرة
كما موصح فى تلك الصورة
حيثتم حذف الB,E,G,I,J
حيث الGC لا يبدأ عملة ألا فى حالة أن الHeap is full
Understanding Object Generation
Object Generation عبارة عن ميكانيزم يستخدم بواسطة الGC لتحسين الاداء حيث عند عمل Collect to unreachable object مش معقول أنة هيبدأ يفحص كل الobjects الموجودة داخل الheap حيث لو كان كدة هياخد وقت طويل وبالأخص فى البرامج الكبيرة وعلشان نحسن الأداء أى object موجود داخل الheap لازم يندرج تحت أى جيل من الاجيال التالية
Generation 0 أى كائن جديد لسة معمول لة create يندرج تحت ذلك الجيل حيث أنة لم يتم فحصة من قبل الGC
Identifies newly allocated object that has never been marked for collection.
كما موضح فى تلك الصورة حيث تم أنشاء مجموعة objects لم يتم فحصها بالGC
دلوقتى لو حبيت أضيف كائنات أخرى وكان الheap مليان وبالتالى يبدأ الGC حيث يبدأ بتحليل للheap وبالذات الgeneration 0 ويتم عمل clean للunreachable objects وباقى الكائنات اللى نجت من عملية ال)clean بسبب وجود App root لها )تندرج تحت الGeneration 1
لو حبينا نوصف الGeneration 1 هيبقى عبارة عن الobjects اللى نجت من عملية التنظيف للgeneration 0 يعنى تم عمل تحقيق معها مرة واحدة بواسطة الGC
كما موضح فى تلك الصورة
حيث الobjects a , b, d,g,j تم نقلهم الى الgeneration 1 وتم إضافة الكائنات الجديدة k,l,m,n
طب لو ضيفت مرة أخرى more objects والمساحة الموجودة فى الgeneration 0 غير كافية وبالتالى سيتم أيضا عمل clean لل generation 1 والكائنات التى نجت من عملية التنظيف للجيل الاول يتم إدراجها تحت الgeneration 2
حيث الgeneration 2 يعتبر Highest generation المدعم بواسطة الruntime garbage collector أما لو المساحة غير كافية يتم عمل clean للجيل التانى والكائنات الناجية من تلك العملية تظل فى ذلك الجيل
كما موضح فى تلك الصورة عند أضافة الo,p objects سيتم عمل clean أولا الى الgeneration 0 حيث المساحة لم تكفى يتم عمل clean للgeneration 1 وأيضا المساحة لم تكفى يتم عمل clean للgeneration2
كما موضح فى تلك الصورة
The System.GC Types
دلوقتى تعالى نشوف الGC class وبعض الدوال الموجودة فية والتى تمكنا بالتحكم فى الGC والتعامل معة حيث يفضل أستخدام تلك الدوال فى حالة الunmanaged resources
MaxGeneration property حيث ترجع أكبر قيمة للاجيال الموجودة هى 2 حيث عندنا ثلاث أجيال 0و1و2 Void GC.Collect(int32 Generation ) حيث تحدد أى جيل عاوز أعمل collect للunreachable objects الموجودة داخلة حيث تستقبل رقم صحيح يبدأ من 0 الى الGC.MaxGeneration حيث عند عمل pass للجيل رقم 0 سيتم عمل collect لة أما عند عمل pass للجيل الاول وبالتالى يتم عمل collect للجيل رقم 0و1 وأيضا عند عمل pass للجيل الثانى فى تلك الحالة سيتم عمل collect to unreachable objects الموجودة داخل كل من الاجيال 0و1و2بالترتيب. Void GC.Collect() فى تلك الحالة تكون متساوية ل GC.Collect(GC.MaxGeneration) وبالتالى سيتم عمل تجميع لجميع الكائنات التى لم يعد لها root فى جميع الاجيال0و1و2 على الترتيب ايضا;-) حيث يفضل ترك عملية الcollect للGC فى حالة الManaged resources أما فى حالة الunmanaged يفضل تعريف الGC كيفية تخليص الmemory من الunmanaged resources Int32 GetGeneration(object obj) Int32 GetGeneration(WeakReference wr) حيث الدالة الاولى يتم أعطائها كائن معين والثانية تأخذ كائن من النوعWeakReference (سنتعرف علية كمان شوية بأذن الله) وكلاهما يرجعان رقم الجيل الموجود بة ذلك الobject GetTotalMemory(Boolean ForceFullCollection) ترجع المساحة المحجوزة للManaged heap وتكون بالبايت وذلك المتغير الboolean لمعرفة هل انتا عاوز تعرف المساحة المستخدمة حاليا ف الmanaged heap بعد ما أعمل collect ولا قبل إذا كان true يبقى أعمل الاول collect وبعد كدة رجعلى المساحة المستخدمة وfalse رجع المساحة بدون ماتعمل collect WaitForPendingFinalizers() معناها أعمل وقوف مؤقت لجميع الthreads النشيطة لحد ما أعمل finalize للobjects حيث تلك الدالة يجب أن تنادى بعد ال()GC.Collect
تعالى نشوف مثال بسيط على تلك الدوال كما موضح
static void Main(string[] args)
{
Employee emp = new Employee("Mohammed Allam", 19);
//emp.PrintData();
Console.WriteLine();
//Get number of generatios
Console.WriteLine("Number of generation is {0}",GC.MaxGeneration+1);
//Print number of bytes on heap
Console.WriteLine("Total memory before collecting operation is {0} bytes",GC.GetTotalMemory(false));
//get generation of emp object.
Console.WriteLine("Emp obj in Generation {0}\n",GC.GetGeneration(emp));
GC.Collect(0);
GC.WaitForPendingFinalizers();
Console.WriteLine("After collect generation 0 :");
Console.WriteLine("Now Emp obj hold in generation {0}\n",GC.GetGeneration(emp));
//Collect to generation 0 and 1
GC.Collect(1);
GC.WaitForPendingFinalizers(); //Temporarily halt for other threads
Console.WriteLine("After collecting generation 0 and 1 :");
Console.WriteLine("Now Emp obj hold in generation {0}\n", GC.GetGeneration(emp));
//Print how many times a generation has been swept.
Console.WriteLine("Gen 0 has been swept {0} times",GC.CollectionCount(0));
Console.WriteLine("Gen 1 has been swept {0} times", GC.CollectionCount(1));
Console.WriteLine("Gen 2 has been swept {0} times", GC.CollectionCount(2));
}والoutput كما موضح فى تلك الصورة
