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

حالا فرض کن یک صندلی قدیمی داری که دیگه ازش استفاده نمیکنی، اما پاش رو با زنجیر بستي به ستون خونه! نظافتچی میاد که صندلی رو ببره، اما چون زنجیر شده، نمیتونه. صندلی همون‌ جا میمونه، جا اشغال میکنه و تو هم نمیتونی مبل جدید بخری. به این اتفاق میگیم Memory Leak یا نشت حافظه. صندلی ‌هایی که نه استفاده میشن و نه دور ریخته میشن، اون‌قدر زیاد میشن که دیگه جایی برای نفس کشیدن توی خونه نمیمونه و تَق! برنامه کرش می‌کنه (Out of Memory Error).

حافظه در اندروید چطور کار می‌کند؟

حافظه در اندروید چطور کار می‌کند؟

در دنیای اندروید، ما دو نوع حافظه اصلی داریم:

  • Stack: مثل جیب کتت میمونه. وسایل کوچیک و دم ‌دستی (متغیرهای محلی) رو سریع میذاری توش و به محض اینکه از اتاق خارج شدی (متد تموم شد)، جیبت خالی میشه.

  • Heap: مثل انباری خونه است. اشیاء بزرگ (مثل Activityها، بیت ‌مپ‌ ها و لیست ‌ها) اینجا هستن. مدیریت این انبار دست همون نظافتچی (GC) هست.

نشت حافظه زمانی رخ میده که: یک شیء در انباری (Heap) دیگه لازم نیست، اما یک نفر از توی جیبش (یک رفرنس زنده) هنوز نخی به اون وصل کرده و ولش نمی‌کنه.

متهمان ردیف اول: چه چیزهایی باعث نشت حافظه می‌شوند؟

بیایید با هم چند تا از کار های "خطرناکی" که باعث میشه نظافتچیِ اندروید نتونه کارش رو انجام بده، مرور کنیم:

۱. استفاده از Contextهای طولانی (Static Context)

فکر کن میخوای آدرس خونه‌ ت رو به رفیقت بدی. به جای اینکه بگی "الان کجام"، کل سند خونه رو (Activity Context) میدی بهش و اون هم سند رو میذاره توی گاو صندوقش (Static Variable). حالا حتی اگه تو از اون خونه اسباب ‌کشی کنی، چون رفیقت سند رو داره، اون خونه هنوز به اسم توئه و سیستم نمیتونه تخریبش کنه!

اشتباه:

public class MyHelper {
    // این متغیر استاتیک تا ابد زنده می‌مونه و Activity رو زندانی می‌کنه!
    public static Context context; 
}

راه حل: همیشه از getApplicationContext() برای کارهای عمومی استفاده کن یا اصلاً Context رو استاتیک نکن.

۲. کلاس‌های داخلی (Inner Classes) و تردها

این یکی خیلی رایجه. فرض کن توی اکتیویتی یک "تایمر" یا "Thread" راه میندازی که بره از اینترنت دیتا بیاره. کاربر دکمه بازگشت رو میزنه و اکتیویتی بسته میشه. اما اون ترد هنوز داره کار میکنه و چون "بچه" اون اکتیویتیه، دستش رو گرفته و نمیذاره پاک بشه.

اشتباه:

new Thread(new Runnable() {
    @Override
    public void run() {
        // انجام یک کار طولانی...
        // این ترد یک رفرنس مخفی به Activity داره!
    }
}).start();

چطور جلوی این فاجعه را بگیریم؟ (ابزار جادویی)

همون ‌طور که برای چک کردن لوله‌ ها از تعمیرکار استفاده میکنی، برای کد ها هم ابزار های خفنی داریم:

  1. LeakCanary: این کتابخانه مثل یک سگ شکاری میمونه. به محض اینکه توی برنامه نشتی پیدا کنه، یک نوتیفیکیشن بهت میده و دقیقاً نشون میده کدوم "زنجیر" باعث شده شیء مورد نظر پاک نشه.

  2. WeakReference: این مثل یک گره شُله! به GC میگه: "من به این وسیله وصل هستم، اما اگه دیدی جا کمه و کسی دیگه لازمش نداره، میتونی بندازیش دور، من ناراحت نمیشم."

اصلاح کد با WeakReference:

// به جای نگه داشتن مستقیم اکتیویتی، از رفرنس ضعیف استفاده کن
WeakReference<Activity> activityRef = new WeakReference<>(myActivity);

Activity activity = activityRef.get();
if (activity != null) {
    // حالا با خیال راحت کارتو انجام بده
}

چک‌لیست نهایی برای یک اندروید کار حرفه‌ای

برای اینکه برنامه‌ت مثل بنز نرم کار کنه و حافظه رو قورت  نده، این چند تا قانون رو یادت نره:

  • آن‌سایبکرایب کن: اگه توی onCreate چیزی رو شروع کردی (مثل Listener یا Observer)، حتماً توی onDestroy قطعش کن.

  • استاتیک ممنوع: تا جای ممکن از متغیرهای static برای نگه داشتن اشیاء اندرویدی (مثل View و Activity) استفاده نکن.

  • بیت‌مپ‌های غول‌آسا: عکس‌های سنگین رو وقتی لازم نداری با دستور recycle() آزاد کن.

  • از ابزارها نترس: حتماً LeakCanary رو توی نسخه دیباگ پروژه‌ت نصب کن.

جمع‌بندی

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