۳۰ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

زباله جمع آوری چیست؟ مدیریت خودکار حافظه برای برنامه های شما

بیشتر زبان های برنامه نویسی مدرن از جمع آوری زباله استفاده می کنند، اما توسعه دهندگان گزینه هایی برای نحوه پیاده سازی و تنظیم آن دارند. یک نمای کلی از نحوه کار جمع آوری زباله در زبان هایی مانند جاوا، پایتون و سی شارپ دریافت کنید.

بیشتر زبان های برنامه نویسی مدرن از جمع آوری زباله استفاده می کنند، اما توسعه دهندگان گزینه هایی برای نحوه پیاده سازی و تنظیم آن دارند. یک نمای کلی از نحوه کار جمع آوری زباله در زبان هایی مانند جاوا، پایتون و سی شارپ دریافت کنید.

This article introduces garbage collection, including an overview of garbage collection algorithms and how garbage collection is implemented in some popular programming languages including Java and Python. قبل از پرداختن به آن، اجازه دهید مزایا و معایب خود جمع آوری زباله را در نظر بگیریم. چرا این یک راه حل رایج برای خطاهای تخصیص حافظه است؟ ما با خطرات مدیریت حافظه در زبان هایی مانند C و C++ که از جمع آوری زباله استفاده نمی کنند، شروع می کنیم.

خطرات مدیریت حافظه در C/C++

مشکلات تخصیص حافظه تنها زیرمجموعه‌ای از مشکلات مشترک C/C++ هستند که باعث ایجاد باگ‌ها و آسیب‌پذیری‌های احتمالی می‌شوند، اما زیرمجموعه بزرگی هستند و ردیابی و رفع آن بسیار آزاردهنده است. اشکالات تخصیص حافظه شامل حالات زیر است:

  • ناتوانی در آزادسازی حافظه ای که اختصاص داده اید، که در نهایت می تواند تمام RAM سیستم را مصرف کند و نه تنها برنامه بلکه کل رایانه را خراب کند.
  • تلاش برای خواندن یا نوشتن بافر از طریق یک اشاره گر پس از آزاد شدن حافظه با نتایج بالقوه تصادفی (معروف به نشانگر آویزان).
  • آزادسازی مضاعف یک بلوک حافظه، که می‌تواند مدیر حافظه و در نهایت برنامه یا حتی کل سیستم را خراب کند.

دیگر آسیب‌پذیری‌های رایج C/C++ عبارتند از overrun بافر و دستکاری رشته، که می‌تواند کد را با داده بازنویسی کند. بخش “سرگرم کننده” زمانی است که یک مهاجم داده ها را طوری ایجاد می کند که کد اجرایی مخرب باشد و سپس موفق به اجرای کد می شود.

صبر کنید، صبر کنید، شما می گویید: این دیگر نمی تواند به دلیل کدها و بخش های داده جداگانه در یک سیستم حالت محافظت شده رخ دهد. متأسفانه، هنوز هم می تواند رخ دهد. و در برخی شرایط اتفاق می افتد. یک مثال برنامه ای است که یک دستور SQL را در یک رشته می سازد و سپس آن را برای اجرا به پایگاه داده ارسال می کند و اغلب آسیب پذیری تزریق SQL ایجاد می کند. مطمئناً، بهترین روش‌های مستند برای جلوگیری از آسیب‌پذیری‌های تزریق SQL وجود دارد، اما باگ‌های جدید در این دسته همچنان در مشتریان پایگاه داده ظاهر می‌شوند، بنابراین واضح است که هر برنامه‌نویسی بهترین شیوه‌ها را دنبال نمی‌کند.

جمع آوری زباله: درمان معیوب

استفاده از زباله جمع‌آوری می‌تواند مشکلات عمده تخصیص و تخصیص حافظه را کاملاً از بین ببرد، اما هزینه دارد. بزرگترین مسائل مربوط به سربار زباله جمع کن است. غرفه های غیرقابل پیش بینی وقتی زباله جمع کن تصمیم می گیرد بدود. و افزایش تاخیر زمانی که فرآیند سرور متوقف می شود. مشکل اخیر اغلب در برنامه های سرور مبتنی بر جاوا نشان داده می شود.

سربار جمع‌آوری زباله می‌تواند قابل توجه باشد و شامل یک مبادله بین حافظه و عملکرد است. طبق مقاله در سال ۲۰۰۵ توسط متیو هرتز و امری دی برگر:

کلکسیونرهای نسلی به سبک اپل، جمع آوری کنندگان زباله محافظه کار هستند. GC های تهاجمی تر گاهی اوقات می توانند با حافظه کمتر عملکرد بهتری داشته باشند.

توقف و تأخیر به این معنی است که زبان‌های مبتنی بر GC می‌توانند برای برنامه‌های هم‌زمان و سرورهای پرتوانی که نیاز به به حداقل رساندن تأخیر دارند، کمتر از حد مطلوب باشند. برای مثال، چندین تلاش برای Lisp بلادرنگ و جاوا بلادرنگ انجام شده است که همگی جمع‌آوری زباله را تغییر داده یا حذف کرده‌اند.

اخیراً، چندین سرور جاوا و اسکالا به زبان‌های غیر GC بازنویسی شدند، برای مثال Scylla، که بازنویسی Cassandra در C++ است، و Redpanda، که یک جایگزین پلاگین کافکا که عمدتاً در C++ نوشته شده است. در هر دو Scylla و Redpanda، تاخیر در مقایسه با سرورهای اصلی به شدت کاهش یافته است. هر دو نیز به خوشه های بسیار کوچکتری برای بار یکسان نیاز دارند.

الگوریتم های جمع آوری زباله

ده ها الگوریتم برای جمع آوری زباله وجود دارد. بیایید به برخی از مهم ترین الگوریتم ها و ویژگی های برجسته آنها نگاه کنیم.

شمارش مرجع

در شمارش مراجع، برنامه تعداد ارجاعات، اشاره گرها یا دسته های یک منبع را به عنوان بخشی از منبع تخصیص داده شده ذخیره می کند و با افزودن یا حذف مراجع، تعداد آن را افزایش یا کاهش می دهد. وقتی تعداد مراجع به صفر رسید، می توان منبع را آزاد کرد. جمع آوری زباله های حافظه تنها یکی از کاربردهای شمارش مرجع است. همچنین برای کنترل توزیع اشیاء سیستم، اشیاء COM ویندوز و بلوک‌ها یا فایل‌های سیستم فایل استفاده می‌شود.

شمارش مراجع دو جنبه منفی دارد: به‌روزرسانی‌های مکرر و مراجع دایره‌ای. یکی از راه‌های کنترل فراوانی به‌روزرسانی‌ها، اجازه دادن به کامپایلر برای دسته‌بندی اشیاء مرتبط است. یکی از راه‌های مدیریت ارجاعات دایره‌ای، که شمارش را از رسیدن به صفر جلوگیری می‌کند، اجرای گاه به گاه GC ردیابی برای حذف چرخه‌های غیرقابل دسترس است.

ردیابی جمع آوری زباله

Tracing GC جایگزین اصلی برای شمارش مرجع است و شامل همه الگوریتم‌های زیر و همچنین تعداد کمی دیگر است. ایده کلی ردیابی جمع‌آوری زباله این است که فرآیند ردیابی با برخی از اشیاء ریشه (مانند متغیرهای محلی فعلی، متغیرهای سراسری و پارامترهای تابع فعلی) شروع می‌شود و برای تعیین اینکه کدام اشیاء قابل دسترسی هستند، از مراجع پیروی می‌کند. سپس همه اشیاء غیرقابل دسترس جمع آوری می شوند. ردیابی جمع آوری زباله آنقدر رایج است که گاهی اوقات به سادگی جمع آوری زباله نامیده می شود.

علامت گذاری و جارو کردن

الگوریتم علامت گذاری و جاروب “ساده لوح”، که در سال ۱۹۶۰ منتشر شد و به جان مک کارتی و لیسپ، ابتدا سیستم را منجمد می‌کند، سپس تمام اشیاء قابل دسترسی از مجموعه ریشه را به‌عنوان «در حال استفاده» علامت‌گذاری می‌کند. مرحله سوم این است که تمام حافظه را جارو کنید و هر بلوکی را که “in-use” علامت گذاری نشده است، آزاد کنید. در نهایت، بیت “در حال استفاده” در تمام بلوک های حافظه باقیمانده پاک می شود تا برای مجموعه بعدی آماده شود، و سیستم مجاز است. بدیهی است که این برای سیستم های بلادرنگ نامناسب است.

یک نوع در علامت گذاری و جابجایی از سه “رنگ” حافظه استفاده می کند: بلوک های سفید غیرقابل دسترسی هستند و اگر با پایان الگوریتم همچنان در مجموعه سفید باشند، محکوم به آزاد شدن هستند. بلوک های سیاه از ریشه قابل دسترسی هستند و هیچ ارجاع خروجی به اشیاء در مجموعه سفید ندارند. بلوک های خاکستری از ریشه قابل دسترسی هستند اما هنوز برای ارجاع به اشیاء “سفید” اسکن نشده اند. پس از تکمیل الگوریتم، بلوک های خاکستری همه در مجموعه سیاه قرار می گیرند. به طور معمول، علامت گذاری اولیه همه بلوک های ارجاع شده توسط ریشه ها را در مجموعه خاکستری و سایر بلوک ها را در مجموعه سفید قرار می دهد.

الگوریتم نوع سه رنگ دارای سه مرحله است:

  1. یک شی را از مجموعه خاکستری انتخاب کنید و آن را به مجموعه سیاه منتقل کنید.
  2. هر جسم سفیدی را که به آن ارجاع می دهد به مجموعه خاکستری منتقل کنید. این تضمین می کند که نه این شی و نه هیچ شیئی که به آن ارجاع می دهد نمی تواند زباله جمع آوری شود.
  3. دو مرحله آخر را تکرار کنید تا مجموعه خاکستری خالی شود.

وقتی مجموعه خاکستری خالی است، همه بلوک های سفید را می توان آزاد کرد. الگوریتم سه رنگ را می توان در پس زمینه در حین اجرای برنامه انجام داد. هنوز سربار وجود دارد، اما “دنیا را متوقف نمی کند.”

در حال کپی کردن مجموعه

ایده کلی کپی کردن مجموعه، معروف به نیمه فضای GC، این است که حافظه را به دو ناحیه با اندازه مساوی به نام‌های «از فضا» و «به فضا» تقسیم می‌کنید. به صورت متوالی در فضای به فضا بلوک می کند تا زمانی که پر شود، و سپس یک مجموعه را اجرا می کند. که نقش های مناطق را جابجا می کند و اشیاء زنده را از فضا به فضا کپی می کند و یک بلوک فضای آزاد (مرتبط با حافظه) باقی می گذارد. استفاده شده توسط همه اشیاء غیرقابل دسترس) در انتهای فضا.

مشکلات کپی کردن مجموعه وجود دارد. بزرگترین مورد این است که وقتی بلوک ها را کپی می کنید آدرس آنها تغییر می کند. یک راه حل برای آن حفظ جدول آدرس های ارسال است. مشکل بزرگ دیگر این است که برای کپی کردن مجموعه به دو برابر حافظه برای علامت گذاری و جارو کردن نیاز دارید. اگر بیشتر حافظه‌ها زباله هستند، مجموعه کپی سریع‌تر از علامت‌گذاری و جارو کردن است، اما اگر بیشتر حافظه قابل دسترسی باشد، کندتر است.

علامت گذاری و فشرده

مجموعه علامت‌گذاری و فشرده اساساً مجموعه‌ای کپی است که در یک فضای حافظه اجرا می‌شود. گردآورنده علامت فشرده تمام اشیاء قابل دسترسی را اسکن می کند و آنها را در پایین پشته فشرده می کند که قسمت بالای پشته را برای مصرف در دسترس می گذارد. بزرگترین ایراد مجموعه علامت گذاری و جمع و جور زمان آن است.

مجموعه نسلی

مجموعه نسلی، پشته را بر اساس سن اشیا، یعنی نسل‌ها، به چند فضا (معمولاً دو یا سه) تقسیم می‌کند. به طور کلی، اشیاء اخیر بیشتر از اشیاء قدیمی‌تر زباله هستند، بنابراین منطقی است که اشیاء جدیدتر را برای یافتن زباله اسکن کنید و اشیای قدیمی‌تر را بیشتر اوقات به حال خود رها کنید. برخی از کلکسیونرهای نسلی از فرکانس های اسکن و/یا الگوریتم های مختلف در نسل های مختلف استفاده می کنند.

چه زبان هایی از جمع آوری زباله استفاده می کنند؟

Lisp از زمان اختراع آن توسط جان مک کارتی در سال ۱۹۵۸ از جمع‌آوری زباله استفاده کرده است. جاوا، اسکالا، پایتون و NET/C# همگی زبان‌های محبوب GC هستند. زبان‌های جمع‌آوری زباله دیگر عبارتند از Go، Ruby، D، OCaml، و Swift نسبتاً جوان، و همچنین زبان‌های قدیمی‌تر Eiffel، Haskell، ML، Modula-3، Perl، Prolog، Scheme و Smalltalk.

جاوا، پایتون و .NET/C# برخی از زبان های برنامه نویسی محبوب هستند که جمع آوری زباله را پیاده سازی می کنند. ماشین مجازی جاوا (JVM) در واقع چهار زباله گرد مختلف را ارائه می دهد: سریال، موازی، علامت گذاری و جابجایی همزمان، و G1GC، اولین زباله گرد. G1GC اکنون پیش فرض در جاوا است. این یک کلکتور متراکم موازی منطقه ای و نسلی است که به اهداف نرم در زمان واقعی دست می یابد.

Python، به‌ویژه اجرای استاندارد CPython، شمارش مرجع را با مجموعه نسل سه سطحی که فقط بر تمیز کردن اشیاء ظرف تمرکز دارد. NET CLR (زمان اجرای زبان رایج) از یک نسل سه سطحی استفاده می کند. الگوریتم جمع آوری علامت و فشرده. CLR همچنین اشیاء حافظه را به دو پشته جدا می کند، یکی برای اشیاء بزرگ (۸۵۰۰۰ بایت یا بالاتر) و دیگری برای اشیاء کوچک. انبوه جسم بزرگ معمولا فشرده نمی شود، فقط علامت گذاری و جارو می شود، اما در صورت لزوم می توان آن را فشرده کرد.

نتیجه گیری

همانطور که مشاهده کردید، روش های زیادی برای مدیریت زباله وجود دارد و بیشتر آنها کاربردهای خود را دارند. پیاده‌سازی‌های جمع‌آوری زباله بالغ‌تر، الگوریتم‌های متعددی را ترکیب می‌کنند و در طول سال‌ها به شدت تنظیم شده‌اند تا تاخیر را به حداقل برسانند.