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

نشت حافظه یا همان (Memory leaks) در برنامه‌های اندروید زمانی رخ می‌دهد که برنامه به اشیائی که دیگر نیازی به آن‌ها نیست، ارجاع می‌دهد و باعث می‌شود جمع‌آوری زباله (garbage collector) نتواند حافظه آن‌ها را بازیابی کند. با گذشت زمان، این نشت‌ها می‌توانند منجر به افزایش استفاده از حافظه و در نتیجه کاهش عملکرد برنامه یا حتی خرابی آن شوند.

نکته : در اینجا منظور از "ارجاع" همان "reference" است که به ارتباط یا اشاره به یک شیء در حافظه اشاره دارد. وقتی می‌گوییم "ارجاع به یک شیء" یعنی یک متغیر یا یک مکان در برنامه به آن شیء خاص اشاره می‌کند و می‌تواند از آن استفاده کند.
جمع‌آوری زباله یا garbage collector چیست؟

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

1. درک نشت حافظه:

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

2. مدیریت چرخه حیات Activity و Fragment:

Activityها و Fragment ها دارای چرخه حیات هستند و مدیریت اشیاء در این چرخه‌ها ضروری است. به عنوان مثال، منابعی مانند اتصالات شبکه یا اتصالات به پایگاه داده باید در متد onDestroy آزاد شوند تا از نگه‌داشتن ارجاعات پس از پایان چرخه حیات کامپوننت جلوگیری شود.

3. با Context محتاط باشید:

  • هنگامی که یک context با عمر کوتاه‌تر (مثل context مربوط به activity) کافی است، از استفاده context برنامه اجتناب کنید.
  • در نگه‌داشتن ارجاعات به context، مواظب باشید زیرا ممکن است منجر به نشت حافظه شود.

4. اجتناب از ارجاعات استاتیک:

  • از متغیرهای استاتیک یا ارجاعات به context خودداری کنید.
  • ارجاعات استاتیک می‌توانند در کل چرخه حیات برنامه باقی بمانند و باعث نشت حافظه شوند.
  • اگر نیاز به استفاده از ارجاعات استاتیک دارید، به جای Activity context از Application context استفاده کنید.

5. استفاده از WeakReferences:

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

6. آزادسازی منابع:

  • منابعی مانند پایگاه‌های داده، فایل‌ها و اتصالات شبکه را به صورت صریح (Explicitly) آزاد کنید.
  • از try-with-resources یا بلوک‌های finally استفاده کنید تا آزادسازی منابع تضمین شود.

7. اجتناب از حافظه‌پنهانی بیش از اندازه:

  • درباره cashing اطلاعات بزرگ هوشیار باشید.
  • از راهکارهای cashing که به طور خودکار اطلاعات قدیمی را حذف یا اندازه cashing را محدود می کند استفاده کنید.
  • اندازه cashing را محدود کرده و از راهکارهایی مانند حذف مبتنی بر زمان یا LRU (کمترین استفاده اخیر (Least Recently Used)) استفاده کنید تا اطمینان حاصل شود که اطلاعات قدیمی حذف می‌شوند.

8. مدیریت درست تغییرات پیکربندی:

  • هنگام مدیریت تغییرات پیکربندی، مانند چرخش‌های صفحه، اطمینان حاصل کنید که ارجاعات شیء را به به درستی مدیریت می شود تا از نشت حافظه جلوگیری شود.
  • از ViewModels یا راهکارهای دیگر استفاده کنید تا اطلاعات را در طول تغییرات پیکربندی نگه دارید بدون اینکه نشت حافظه ایجاد شود.
نکته : تغییرات پیکربندی یا (Handle Config Changes) به تغییراتی اشاره دارد که در ویژگی‌های یک برنامه رخ می‌دهد که می‌تواند بر اساس شرایط مختلف محیطی تغییر کند. به عنوان مثال، زمانی که یک کاربر صفحه نمایش دستگاه خود را چرخانده و از حالت عمودی به افقی یا برعکس تغییر می‌دهد، تغییرات پیکربندی رخ می‌دهد.
در برنامه‌نویسی اندروید، وقتی یک تغییر پیکربندی اتفاق می‌افتد، ممکن است Activity یا Fragment کنونی مجدداً ایجاد شود و مراحل مختلف زندگی آن‌ها مجدداً طی شود. این تغییرات می‌توانند به چالش کشیدن مدیریت اطلاعات مربوط به وضعیت فعلی برنامه منجر شوند، به ویژه اگر ارتباطی با عملکرد فعلی کاربر داشته باشند.
بنابراین، هنگامی که می‌گوییم "مدیریت تغییرات پیکربندی"، منظورمان این است که باید به طور صحیح و از روش‌های مختلف، از جمله استفاده از ViewModels، اطلاعات مربوط به وضعیت و ارتباطات برنامه را در طول تغییرات پیکربندی حفظ کنیم تا از مشکلاتی مانند نشت حافظه جلوگیری شود.

9. به‌روزرسانی Dependencies:

  • وابستگی‌های برنامه‌ی خود، از جمله کتابخانه‌های شخص ثالث، را به‌روز نگه دارید.

10. استفاده از کتابخانه‌های تشخیص نشت حافظه:

  • ابزارهایی مانند LeakCanary می‌توانند به طور خودکار نشت حافظه‌ها را در طول توسعه تشخیص دهند و گزارش دهند.
  • این ابزارها را در فرآیند توسعه‌ی خود استفاده کنید تا بتوانید نشت حافظه‌ها را به سرعت شناسایی کنید.

11. با سینگلتون‌ها محتاط باشید:

  • سینگلتون‌ها ممکن است به طور نامحدود ارجاعات را نگه دارند که منجر به نشت حافظه شود. در نظر داشته باشید از فریمورک های تزریق وابستگی مانند Dagger استفاده کنید تا مدیریت چرخه حیات اشیاء را انجام داده تا نمونه‌های سینگلتون با عمر طولانی خودداری شود.

12. اجتناب از ایجاد اشیاء غیرضروری:

  • از ایجاد اشیاء موقت، به‌ویژه داخل حلقه‌ها، کاهش دهید، زیرا ممکن است منجر به تخصیص بیش از حد حافظه شود.

13. استفاده هوشمندانه از Application Class:

  • هنگامی که اشیاء با عمر طولانی را در Application Class ذخیره می‌کنید، مواظب باشید.
  • اشیاء موجود در اینجا ممکن است در طول چرخه حیات کامل برنامه باقی بمانند.


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


ممنون که تا آخر این پست همراه من بودید ، امیدوارم براتون مفید بوده باشه 🙌🙏✌ (:

بقیه آموزش های من با نام (mister developer) را می توانید در تلگرام و اینستاگرام دنبال کنید!!

کانال تلگرام: mister_developerr

اینستاگرام: mister_developerr

موفق و پیروز باشید