در مقالات قبلی که به بررسی و آشنایی با زبان برنامه نویسی سی شارپ پرداختیم، با نحوه تخصیص و مدیریت حافظه در برنامه های دات نت آشنا شدیم. در این مقاله قصد داریم به صورت تخصصی تر این بحث را ادامه داده و با برخی مفاهیم و سرویس ها برای مدیریت حافظه در دات نت آشنا شویم. ابتدا جهت یادآوری برخی مفاهیم را به صورت خلاصه بررسی می کنیم:
زمانی که شما برنامه ای را به زبان سی شارپ یا هر برنامه مبتنی بر دات نت می نویسید، نگران مدیریت حافظه نیستید. زیر می دانید که سرویس GC کار مدیریت حافظه را برای شما انجام می دهد. تنها کاری که شما می کنید ایجاد شئ بوسیله کلمه کلیدی new بوده و بعد خیالتان بابت همه چیز راحت است! اما سرویس GC از کجا متوجه می شود که شئ ای در حافظه Heap باید حذف شود؟ جواب ساده است: «در صورتی که شئ مورد نظر توسط هیچ یک از بخش های کد قابل دسترس نباشد!». برای مثال، فرض کنید متدی را در کلاس Program تعریف کرده اید که عملیات ایجاد یک شئ را انجام می دهد:
public static void CreatePerson() { var person = new Person(); }
دقت کنید که شئ person، تنها تا زمانی قابل استفاده است که کد داخل متد در حال اجرا است و پس از خروج از متد، دیگر نیازی به شئ person نیست، پس سرویس GC می داند که باید این شئ را از حافظه حذف کند. حال اگر شئ ساخته شده به عنوان مقدار بازگشتی متد استفاده میشد، زمانی شئ person حذف می شود که دیگر نیازی به شئ بازگردانده شده است متد نباشد. وجود قابلیتی مثل GC برای برنامه نویسانی که کار مدیریت حافظه را به صورت دستی انجام می داند.
مثل برنامه نویسان ++C، حکم بهشت را دارد! فقط برنامه نویسان ++C می توانند درک کنند که عملیات مدیریت حافظه در محیط های مدیریت نشده (Unmanaged) چه کار طاقت فرسایی است! اما زمانی که کلمه کلیدی new برای ساخت شئ اجرا می شود، دقیقاً چه اتفاقاتی در پشت پرده رخ می دهد؟ ابتدا بهتر است نگاهی به کد IL ایجاد شده برای ساخت شئ Person در متد CreatePerson داشته باشیم:
.method public hidebysig static void CreatePerson() cil managed { // Code size 8 (0x8) .maxstack 1 .locals init ([0] class OLT.Person person) IL_0000: nop IL_0001: newobj instance void OLT.Person::.ctor() IL_0006: stloc.0 IL_0007: ret } // end of method Program::CreatePerson
حافظه Heap و روند کاری آن چیزی بیشتر از یک حافظه ساده که مقداری در آن ذخیره می شود می باشد! سرویس GC عملیات های زیادی برای Optimzie کردن و استفاده بهینه از Heap انجام می دهد. برای این منظور همیشه در حافظه Heap، یک اشاره گر (pointer) که محل ذخیره سازی شئ بعدی را مشخص می کند وجود دارد که به آن Next Object Pointer یا New Object Pointer می گویند. در کد بالا، عبارت newobj را مشاهده می کنید. این عبارت کارهای زیادی انجام می دهد که در زیر به بررسی آن ها می پردازیم:
در تصویر زیر، نمونه ای از روند ذکر شده را مشاهده می کنید:
برنامه نویسان ++C برای حذف یک شئ از حافظه می بایست مقدار آن را برابر null قرار دهند. شاید برای شما این سوال پیش بیاد که اگر این کار را در زبان سی شارپ و تحت پلاتفرم دات نت انجام دهیم چه اتفاقی می افتد؟ کد زیر را در نظر بگیرید:
public static void CreatePerson() { var person = new Person(); person = null; }
با ابزار ildasm، نگاهی به کد IL می اندازیم:
.method public hidebysig static void CreatePerson() cil managed { // Code size 10 (0xa) .maxstack 1 .locals init ([0] class OLT.Person person) IL_0000: nop IL_0001: newobj instance void OLT.Person::.ctor() IL_0006: stloc.0 IL_0007: ldnull IL_0008: stloc.0 IL_0009: ret } // end of method Program::CreatePerson
یک عبارت جدید به کد ما اضافه شده است، عبارت ldnull. این opCode، مقدار null را در Virtual Execution Stack قرار میدهد که در حقیقت ست کردن مقدار null به متغیر person ما است. موضوعی که ما می خواهیم به آن اشاره کنیم این است که قرار دادن مقدار null در یک متغیر، تضمینی برای اجرای Garbage Collector و آزاد سازی فضای حافظه نیست! تنها کاری که شما انجام می دهید، قطع کردن ارتباط میان متغیر person در حافظه stack و شئ ایجاد شده در حافظه heap است، البته در اولین اجرای GC، شئ مورد نظر از حافظه Heap حذف خواهد شد.
به یاد دارید چگونه GC تشخیص می داد که یک شئ دیگر استفاده نشده و می بایست حذف شود؟ برای آشنایی با جزئیات این مورد، می بایست با مفهومی به نام Application Root آشنا شویم. به زبان ساده، یک root یک مکان ذخیره سازی است که آدرس یک شئ در Heap در آن نگهداری می شود. می توان مفهوم root را در هر یک از دسته های زیر جا داد:
در طول عملیات GC، تمام اشیاء داخل Heap بررسی می شوند تا مشخص شود آن ها توسط کدهای برنامه در دسترس هستند یا خیر. برای این کار، ابتدا CLR یک گراف از شئ ها می سازد که به آن Object Graph می گویند. Object Graph ها برای این ساخته می شوند تا شئ هایی که باید نگهداری شوند شناسایی شوند. توجه کنید، CLR هیچ گاه یک شئ را دوبار در گراف قرار نمی دهد، اتفاقی که در برنامه های COM رخ می دهد که اصطلاحاً در COM به آن Circular Reference گفته می شود.
بعد از تشکیل گراف، مشخص می شود که کدام یک از اشیاء در دسترس Application Root ها هستند. بعد از مشخص شدن اشیاء ای که توسط هیچ Application Root ای Reference ای به آن ها داده نشده، آن ها از حافظه حذف شده و عملیات Defragment بر روی حافظه Heap انجام می شود تا اشیاء داخل Heap مرتب شده و فضای های خالی شده قابل دسترس باشند. این کار نیازمند ست کردن مجدد Application Root ها می باشد.
زمانی که CLR تصمیم به شناسایی اشیاء بلا استفاده می گیرد، عملیات شناسایی بر روی هر شئ ای که در Managed Heap قرار دارد انجام نمی شود. انجام همچین کاری می تواند به شدت بر روی Performance برنامه ها تاثیر بگذارید. برای بهینه سازی این روند، از تکنیلی به نام Object Generation ها استفاده می شود. فلسفه استفاده از این تکنیک خیلی ساده است: هر چقدر شئ ای بیشتر در Heap حضور داشته باشد، احتمال استفاده از آن بیشتر است. برای مثال، در برنامه های ویندوز یک شئ برای فرم اصلی وجود دارد که این شئ از ابتدا تا انتهای برنامه نباید از بین برود. بر اساس همین سناریو، هر شئ بر اساس زمان ایجاد در یک Generation قرار میگیرد. در کل سه Generation مختلف وجود دارد که در زیر آن ها را بررسی می کنیم:
اشیاء موجود در Generation 0، با هر بار اجرای GC بررسی می شوند. اگر نیاز به حذف آن ها باشد، عملیات حذف بر روی آن ها اجرا شده و در غیر اینصورت به Generation 1 منتقل می شوند. اگر با حذف اشیاء Generation 0 نیاز به حافظه بیشتری باشد، اشیاء موجود در Generation 1 بررسی شده و اشیاء نجات پیدا کرده از Generation 1 به Generation 2 منتقل می شوند. این پروسه برای Generation 1 و Generation 2 هم تکرار می شود تا فضای مورد نیاز برای ایجاد شئ فراهم شود.بحث مربوط به مدیریت حافظه در برنامه های دات نت بسیار گسترده می باشد. در قسمت بعدی در مورد کلاس System.GC صحبت خواهیم کرد. امیدوارم که این مطلب مورد توجه دوستان قرار گرفته باشد.
بنیانگذار توسینسو و برنامه نویس و توسعه دهنده ارشد وب
حسین احمدی ، بنیانگذار TOSINSO ، توسعه دهنده وب و برنامه نویس ، بیش از 12 سال سابقه فعالیت حرفه ای در سطح کلان ، مشاور ، مدیر پروژه و مدرس نهادهای مالی و اعتباری ، تخصص در پلتفرم دات نت و زبان سی شارپ ، طراحی و توسعه وب ، امنیت نرم افزار ، تحلیل سیستم های اطلاعاتی و داده کاوی ...
زمان پاسخ گویی روز های شنبه الی چهارشنبه ساعت 9 الی 18
فقط به موضوعات مربوط به محصولات آموزشی و فروش پاسخ داده می شود