عنوان:

‫مدیریت حافظه و جمع‌آوری زباله (Garbage Collection) - قسمت پنجم


نویسنده: امیر مکارچی
تاریخ: ۱۴۰۳/۱۱/۱۹ ۱۸:۳۰
آدرس: www.dntips.ir
در الگوریتم‌های قبلی، مثل Mark-Sweep، بیت‌های علامت‌گذاری (Mark Bits) معمولاً در هدر اشیاء ذخیره می‌شدند. اما در روش Bitmap Marking، بیت‌های علامت‌گذاری بجای هدر اشیاء، در یک جدول جانبی (Bitmap Table) ذخیره می‌شوند. این تغییر، مزایا و چالش‌هایی را به‌همراه دارد.

Bitmap Marking
در این روش، برای هر آدرسی که ممکن است یک شیء در آن ذخیره شود، یک بیت در جدول Bitmap اختصاص داده می‌شود. این بیت مشخص می‌کند که آیا شیء، در همان آدرس علامت‌گذاری شده است یا خیر.
مزایای اصلی این روش عبارتند از:
  1. صرفه‌جویی در فضا: بیت‌های علامت‌گذاری در یک جدول فشرده ذخیره می‌شوند؛ نه در هدر هر شیء که این باعث کاهش مصرف حافظه می‌شود.
  2. کاهش تغییرات در حافظه: چون بیت‌های علامت‌گذاری جدا از اشیاء هستند، تعداد دسترسی‌های به کش و حافظه کمتر می‌شود.
  3. عملکرد بهتر در اسکن: می‌شود چند بیت مربوط به یک دسته از اشیاء را همزمان بررسی کرد. این کار سرعت اسکن را افزایش می‌دهد.

چالش‌ها:
  1. مدیریت رقابت (Races): اگه چندین Thread بخواهند همزمان بیت‌های Bitmap را تغییر بدهند، ممکن است مشکلاتی مثل از دست دادن به‌روزرسانی‌ها (Lost Updates) پیش بیاید. این چالش در سیستم‌های Multi Thread مهم است.
  2. پیچیدگی دسترسی: دسترسی به بیت‌های Bitmap ممکن است که به‌خاطر وابستگی به کش یا مسائل صفحه‌بندی (Paging) هزینه‌بر باشد.

چند راهکار برای بهبود Bitmap Marking وجود دارد:
استفاده از Byte Map بجای Bitmap
بجای استفاده از یک بیت برای هر آدرس، می‌شود از یک بایت استفاده کرد. این کار باعث می‌شود تغییرات نادرست به‌خاطر رقابت‌ها کمتر شود، هرچند که مصرف حافظه بیشتر می‌ گردد.
محل قرارگیری Bitmap
بجای ذخیره Bitmap در یک مکان ثابت در هر بلوک، می‌شود مکان آن را با استفاده از یک هش (Hash) محاسبه کرد. این کار به کاهش مشکلات کش و صفحه‌بندی کمک می‌کند.
ادغام با ساختارهای دیگر
برخی سیستم‌ها از ترکیب روش Bitmap با هدر اشیاء استفاده می‌کنند. مثلاً یک بایت برای هر بلوک در کنار بیت‌های هدر اشیاء ذخیره می‌شود تا اطلاعات بیشتری داشته باشند.

این روش کجاها استفاده می‌شود:
  1. Conservative Collectors: در زبان‌هایی مثل C که اطلاعات دقیقی درباره ارجاعات نداریم، Bitmap کمک می‌کند که تصمیمات محافظه‌کارانه‌تری بگیریم.
  2. مدیریت خوشه‌های اشیاء: چون اشیاء معمولاً به صورت خوشه‌ای (Clusters) زنده یا مرده هستند، Bitmap می‌تواند تعیین کند که یک بلوک کامل از اشیاء قابل آزادسازی هست یا خیر.

کاربردهای پیشرفته
این روش در کلکتورهای پیشرفته هم کاربرد دارد. به چه صورت؟
  • کاهش سربار در پشته علامت‌گذاری (Mark Stack): در کلکتورهای همزمان، می‌شود از Bitmap برای جایگزینی کامل یا جزئی پشته علامت‌گذاری استفاده کرد.
  • مدیریت سرریز پشته (Stack Overflow): وقتی پشته پر شود، می‌شود به کمک Bitmap، به‌راحتی اشیای علامت‌گذاری شده را پیدا کرد و فرآیند را ادامه داد.

mark():
    cur ← nextInBitmap()
    while cur < HeapEnd  /*marked ref is black if and only if ref < cur */
        add(worklist, cur)
        markStep(cur)
        cur ← nextInBitmap()

markStep(start):
    while not isEmpty(worklist)
        ref ← remove(worklist)  /*ref is marked*/
        for each fld in Pointers(ref)
            child ← *fld
            if child ≠ null && not isMarked(child)
                setMarked(child)
                if child < start
                    add(worklist, child)
در اینجا مراحل الگوریتم به طور خلاصه توضیح داده می‌شوند:
تابع mark، شروع به علامت‌گذاری اشیاء می‌کند. cur نشان‌دهنده‌ی شیء فعلی است که از طریق nextInBitmap دریافت می‌شود. تا زمانیکه cur به انتهای هیپ (HeapEnd) نرسیده باشد، مراحل زیر تکرار می‌شود: شی فعلی (cur) به worklist اضافه می‌شود. تابع markStep(cur) برای علامت‌گذاری شیء و اشیاء مرتبط با آن فراخوانی می‌شود. شیء بعدی از طریق nextInBitmap دریافت می‌شود. تابع markStep(start) اشیاء را از worklist برمی‌دارد و علامت‌گذاری می‌کند. تا زمانیکه worklist خالی نباشد، مراحل زیر تکرار می‌شود: یک شیء (ref) از worklist حذف می‌شود. برای هر اشاره‌گر (fld) در شیء ref، شیء فرزند (child) بررسی می‌شود. اگر شیء فرزند قبل از start قرار داشته باشد (child < start)، به worklist اضافه می‌شود.