آشنایی با Garbage Collector در فلاتر (Flutter) – راهنمای پیشرفته


فلاتر از Dart به عنوان یک زبان توسعه و همچنین یک «سیستم زمان اجرا» (runtime) استفاده میکند. سیستم زمان اجرای دارت به صورت «همیشه حاضر» (ever-present) هم در حالت دیباگ و هم انتشار است؛ اما تفاوتهای بزرگی بین این دو build flavor وجود دارد. در حالت دیباگ اغلب مراحل کار دارت روی دستگاه صورت میگیرند که شامل زمان اجرای دارت، کامپایلر/مفسر در جا (JIT برای اندروید و مفسر برای iOS) و سرویسهای دیباگ و پروفایل کردن است. در حالت انتشار، سرویس JIT/مفسر و دیباگ کردن جدا میشوند؛ اما زمان اجرا باقی میماند و این عاملی است که بیشترین نقش را در اندازه پایه اپلیکیشن فلاتر دارد.

سیستم زمان اجرای دارت شامل یک Garbage Collector است که مؤلفهای ضروری برای تخصیص و رهاسازی حافظه محسوب میشود زیرا اشیا مداوماً وهلهسازی شده و از حافظه خارج میشوند.
فلاتر میتواند اشیای بسیار زیادی داشته باشد. ویجتهای بیحالت زمان رندر شدن روی صفحه ایجاد میشوند و زمانی که حالت اپلیکیشن تغییر مییابد یا زمانی که دیگر پدیدار نیستند، تخریب شده و مجدداً ایجاد میشوند و اغلب آنها عمر کوتاهی دارند. در مورد اپلیکیشنی که رابط کاربری نسبتاً پیچیدهای دارند این وضعیت ممکن است موجب ایجاد هزاران ویجت شود.
اینک سؤال این است که آیا توسعهدهندگان فلاتر باید از Garbage Collector بترسند؟ آیا اکنون که میدانیم فلاتر با تناوب بالایی اشیا را ایجاد و تخریب میکند، باید گامهایی در جهت محدودسازی این وضعیت برداریم؟ در اغلب مواد میبینیم که توسعهدهندگان تازهکار فلاتر ارجاعهایی به ویجتهایی ایجاد میکنند که میدانند در طی زمان تغییر نخواهند یافت و آنها را در حالت (State) قرار میدهند تا تخریب و مجدداً ایجاد نشوند. شما نباید این کار را انجام دهید.
ترس از Garbage Collector تا حدود زیادی بیپایه است، چون معماری آن نسلی (Generational) است و پیادهسازی آن نیز برای ایجاد و تخریب سریع اشیا بهینهسازی شده است. در اغلب وضعیتها باید اجازه دهیم که موتور فلاتر همه ویجتهایی را که دوست دارد ایجاد یا تخریب کند.
Garbage Collector در دارت
Garbage Collector در دارت به صورت Generational است و شامل دو مرحله است: «کاوش فضای جدید» (Young Space Scavenger) و «علامتگذاری موازی و جمعآوریکنندههای روبشی» (Parallel Mark Sweep Collectors).
e
زمانبندی
جهت کمتر کردن تأثیرهای «گردآوری زباله» (Garbage Collection) روی اپلیکیشن و عملکرد رابط کاربری، Garbage Collector قلابهایی به موتور فلاتر ایجاد میکند که وقتی موتور تشخیص دهد اپلیکیشن بیکار است و کاربر تعاملی با آن ندارد، شروع به کار میکند. بدین ترتیب فرصتی در اختیار Garbage Collector قرار میگیرد تا فازهای گردآوری زباله خود را بدون ایجاد اشکالی در عملکرد اپلیکیشن اجرا کند.
Garbage Collector میتواند فشردهسازی لغزشی را نیز در طی این بازههای بیکاری اجرا کند و بدین ترتیب سربار حافظه را با کاستن از تکه تکه شدن (Fragmentation) حافظه کاهش دهد.
مرحله کاوش فضای جدید
این مرحله برای پاکسازی اشیای با عمر کوتاه مانند ویجتهای بیحالت استفاده میشود. با این که این مرحله مسدودکننده است؛ اما بسیار سریعتر از مرحله دوم علامتگذاری/روبش است. مرحله دوم وقتی همراه با زمانبندی اجرا شود باعث ایجاد مکثهایی در عناصر بصری اپلیکیشن در زمان اجرا میشود.
اشیا بر اساس ماهیت خود در فضای پیوستهای از حافظه تخصیص مییابند و زمانی که اشیا ایجاد میشوند به فضای موجود بعدی حافظه تخصیص مییابند تا زمانی که کل فضای تخصیصیافته پر شود. دارت از تخصیص اشارهگر bump برای تخصیص سریع در فضای جدید استفاده میکند که موجب افزایش سرعت قابلملاحظهای در این فرایند میشود.
فضای جدید هنگام تخصیص یافتن اشیای جدید شامل دو نیمه است که به نام نیم فضا شناخته میشوند. هر بار تنها یک نیمه استفاده میشود. در حالی که یک نیمه فعال است، نیمه دیگر غیر فعال باقی میماند. اشیای جدید در نیمه فعال تخصیص مییابند و زمانی که نیمه دیگر پر شود، اشیای زنده از نیمه فعال به نیمه غیر فعال کپی میشوند و اشیای مرده نادیده گرفته میشوند. سپس نیمه غیر فعال، فعال میشود و این فرایند تکرار میشود.
برای تعیین این که اشیا زنده یا مرده هستند، Collector از اشیای ریشه مانند متغیرهای پشته آغاز میکند و آنچه را ارجاع دادهاند بررسی میکند. سپس اشیای ارجاع یافته را انتقال میدهد. در این مرحله بررسی میکند که اشیای ارجاع یافته به کجا اشاره میکنند و این اشیای ارجاع یافته را انتقال میدهد. این فرایند تا زمانی که همه اشیای زنده انتقال یابند تداوم مییابد. اشیای مرده دیگر هیچ ارجاعی ندارند و از این رو در آنجا باقی میمانند و در ادامه وقتی garbage collection بعدی رخ بدهد، اشیای زنده روی آنها کپی میشوند.

علامتگذاری موازی و روبش همزمان
زمانی که اشیا به عمر معینی برسند به فضای حافظه جدیدی انتقال مییابند که از سوی collector نسل دوم یعنی mark-sweep مدیریت میشود.
تکنیک گردآوری زباله در این بخش دو مرحله دارد: ابتدا گراف شیء پیمایش میشود و اشیایی که هنوز مورد استفاده هستند علامتگذاری میشوند. در طی مرحله دوم، کل حافظه اسکن میشود و هر شیئی که علامتگذاری نشده است حذف میشود. سپس همه فلگها پاک میشوند.
این شکل از گردآوری زباله در مرحله علامتگذاری موجب انسداد میشود، چون هیچ تغییری در حافظه نمیتواند رخ بدهد و نخ رابط کاربری مسدود میشود. این گردآوری، فراوانی کمی دارد و اشیای با عمر کوتاه عموماً از سوی «کاوش فضای تازه» مدیریت میشود؛ اما مواردی وجود دارند که سیستم زمان اجرای دارت باید معلق شود تا این نوع گردآوری زباله اجرا شود. با توجه به توانایی فلاتر برای زمانبندی گردآوری، تأثیر این وضعیت اندک خواهد بود.
لازم به ذکر است که اگر یک اپلیکیشن از «فرضیه نسل ضعیف» تبعیت نکند، یعنی اغلب اشیای آن در سن کم نمیرند، در این صورت این شکل از گردآوری به مراتب بیشتر رخ میدهد. با توجه به شیوه پیادهسازی ویجتهای فلاتر این احتمال اندک است؛ اما نکتهای است که باید در خاطر داشت.
ایزولتها (Isolates)
لازم به ذکر است که ایزولت های فلاتر حافظه هیپ اختصاصی خود را دارند که مستقل از همدیگر هستند. از آنجا که هر ایزولت در یک نخ مجزا اجرا میشود، رویدادهای گردآوری زباله برای هر ایزولت تأثیری روی عملکرد ایزولت های دیگر نخواهد داشت. استفاده از ایزولت ها جهت جلوگیری از انسداد رابط کاربری و کم کردن بار پردازش فعالیتهای سنگین مناسب است.
سخن پایانی
در این نوشته به بررسی روش استفاده از Garbage Collector قدرتمند برای کاستن از تأثیرهای انسدادی رویداد گردآوری زباله در اپلیکیشنهای فلاتر پرداختیم. بنابراین نباید از Garbage Collector بترسید چون به بهبود کارکرد اپلیکیشن شما بسیار کمک میکند.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی اندروید
- آموزش برنامهنویسی مقدماتی اندروید
- مجموعه آموزشهای برنامهنویسی
- مفاهیم مقدماتی فلاتر (Flutter) — به زبان ساده
- گوگل فلاتر (Flutter) از صفر تا صد — ساخت اپلیکیشن به کمک ویجت
- آموزش ListView و ScrollPhysics در فلاتر (Flutter) — از صفر تا صد
- آموزش فلاتر (Flutter): توسعه اپلیکیشن برای صفحات نمایش با ابعاد مختلف
==