مدیریت حافظه برنامه

این صفحه نحوه کاهش فعال مصرف حافظه در برنامه شما را توضیح می‌دهد. برای اطلاعات بیشتر در مورد نحوه مدیریت حافظه توسط سیستم عامل اندروید، به «مروری بر مدیریت حافظه» مراجعه کنید.

حافظه با دسترسی تصادفی (RAM) یک منبع ارزشمند برای هر محیط توسعه نرم‌افزار است و برای یک سیستم عامل موبایل که در آن حافظه فیزیکی اغلب محدود است، حتی ارزشمندتر نیز می‌شود. اگرچه هم Android Runtime (ART) و هم ماشین مجازی Dalvik به طور معمول عملیات جمع‌آوری زباله را انجام می‌دهند، اما این بدان معنا نیست که می‌توانید زمان و مکان تخصیص و آزادسازی حافظه توسط برنامه خود را نادیده بگیرید. شما همچنان باید از ایجاد نشت حافظه - که معمولاً ناشی از نگه داشتن ارجاعات به اشیاء در متغیرهای عضو استاتیک است - جلوگیری کنید و هرگونه شیء Reference را در زمان مناسب، همانطور که توسط فراخوانی‌های چرخه عمر تعریف شده است، آزاد کنید.

کاهش حجم کد و منابع برنامه شما

برخی از منابع و کتابخانه‌های درون کد شما می‌توانند بدون اینکه شما متوجه شوید، حافظه را مصرف کنند. اندازه کلی برنامه شما، از جمله کتابخانه‌های شخص ثالث یا منابع تعبیه‌شده، می‌تواند بر میزان مصرف حافظه برنامه شما تأثیر بگذارد. شما می‌توانید با حذف اجزا، منابع و کتابخانه‌های اضافی، غیرضروری یا حجیم از کد خود، مصرف حافظه برنامه خود را بهبود بخشید.

با فعال کردن R8، حجم کلی برنامه را کاهش دهید

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

شما می‌توانید از R8 برای کاهش فضای اشغال شده توسط حافظه برنامه خود استفاده کنید . در حالی که R8 به طور سنتی به دلیل کاهش اندازه APK شناخته می‌شود، تأثیر مستقیم و مثبتی بر حافظه زمان اجرا (RAM) دارد. R8 بایت‌کد برنامه شما را تجزیه و تحلیل می‌کند تا کدهای مرده را حذف کند، کلاس‌های اضافی، متدهای درون‌خطی را ادغام کند و شناسه‌ها را کوچک کند. با بارگذاری بایت‌کدهای کامپایل شده کمتر از APK در RAM، فضای اشغال شده کلی حافظه پایه برنامه را کاهش می‌دهد. علاوه بر این، کوچک‌سازی نام کلاس‌ها، متدها و فیلدها به شناسه‌های کوتاه‌تر، مستقیماً سربار RAM را کاهش می‌دهد. بهینه‌سازی‌هایی مانند ادغام کلاس‌ها و درون‌خطی گسترده متدها، جایگزین جستجوهای پرهزینه زمان اجرا و الگوهای تخصیص نیز می‌شوند و در نتیجه حافظه heap و stack بهینه می‌شود.

قوانین حفظ را درک کنید

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

قوانین Keep که به طور ضعیف نوشته شده‌اند، مانع از آن می‌شوند که R8 بخش‌های بزرگی از پایگاه کد شما را بهینه کند. از قوانین Keep بیش از حد کلی خودداری کنید و این بهترین شیوه‌ها را دنبال کنید:

  • قوانین جهانی که باید از آنها اجتناب کرد:
    • -dontoptimize : بهینه‌سازی را برای کل برنامه کاملاً غیرفعال می‌کند و در نتیجه فایل‌های اجرایی بزرگ‌تر و کندتر می‌شوند.
    • -dontshrink : از حذف کدها و منابع بلااستفاده جلوگیری می‌کند.
    • -dontobfuscate : از کوچک‌سازی نام جلوگیری می‌کند و باعث از دست رفتن صرفه‌جویی ارزشمند در حافظه می‌شود (به‌خصوص در برنامه‌های بزرگ).
  • از استفاده از wildcardها در کل پکیج خودداری کنید: قوانین کلی مانند -keep class com.example.package.** { *; } R8 را مجبور می‌کند تا هر کلاس، فیلد و متد موجود در آن پکیج را حفظ کند. این کار به طور کامل توانایی R8 را برای حذف، بهینه‌سازی یا کوچک‌سازی کد در آن پکیج متوقف می‌کند.

  • از فایل پیکربندی پیش‌فرض R8 استفاده کنید: همیشه proguard-android-optimize.txt استفاده کنید.

برای اطلاعات بیشتر در مورد نوشتن قوانین Keep، به مرور کلی قوانین Keep مراجعه کنید. برای الگوهای خاص برای استفاده و اجتناب از آنها، به بهترین شیوه‌های Keep rules مراجعه کنید.

تحلیلگر پیکربندی R8 بینش‌هایی در مورد پیکربندی R8 شما و نحوه تأثیر هر قانون keep بر برنامه شما ارائه می‌دهد. برای اطلاعات بیشتر در مورد نحوه شناسایی قوانینی که بهینه‌سازی را مسدود می‌کنند، به تحلیلگر پیکربندی R8 مراجعه کنید.

در استفاده از کتابخانه‌های خارجی احتیاط کنید

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

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

اگرچه بهینه‌سازی برنامه شما با استفاده از R8 می‌تواند کد استفاده نشده را از وابستگی‌ها حذف کند، اما اثربخشی آن اغلب توسط پیکربندی داخلی کتابخانه محدود می‌شود. به عنوان مثال، قوانین نگهداری گسترده یا استفاده از بازتاب در یک کتابخانه می‌تواند مانع از کوچک شدن کد توسط R8 شود و منجر به اشغال فضای حافظه بیشتری شود. برای استراتژی‌های انتخاب کتابخانه‌های کارآمد، به بخش «انتخاب هوشمندانه کتابخانه‌ها» مراجعه کنید.

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

برای تزریق وابستگی از Hilt یا Dagger 2 استفاده کنید

چارچوب‌های تزریق وابستگی می‌توانند کدی را که می‌نویسید ساده کنند و محیطی تطبیق‌پذیر فراهم کنند که برای آزمایش و سایر تغییرات پیکربندی مفید است.

اگر قصد دارید از یک چارچوب تزریق وابستگی در برنامه خود استفاده کنید، استفاده از Hilt یا Dagger را در نظر بگیرید. Hilt یک کتابخانه تزریق وابستگی برای اندروید است که بر روی Dagger اجرا می‌شود. Dagger از reflection برای اسکن کد برنامه شما استفاده نمی‌کند. می‌توانید از پیاده‌سازی استاتیک زمان کامپایل Dagger در برنامه‌های اندروید بدون هزینه زمان اجرا یا استفاده غیرضروری از حافظه استفاده کنید.

سایر چارچوب‌های تزریق وابستگی که از بازتاب استفاده می‌کنند، با اسکن کد شما برای حاشیه‌نویسی‌ها، فرآیندها را مقداردهی اولیه می‌کنند. این فرآیند می‌تواند به چرخه‌های CPU و RAM بسیار بیشتری نیاز داشته باشد و می‌تواند باعث تأخیر قابل توجهی در هنگام راه‌اندازی برنامه شود.

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

در بارگذاری تصاویر هدفمند باشید

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

برای مثال، اکثر بیت‌مپ‌ها از پیکربندی ARGB_8888 استفاده می‌کنند، به این معنی که هر پیکسل به ۴ بایت حافظه نیاز دارد - یک بایت برای هر رنگ قرمز، سبز، آبی و آلفا (شفافیت). اگر یک فایل JPEG با حجم ۱۰۰ کیلوبایت داشته باشید و آن را در یک نمای ۱۰۰۰×۱۰۰۰ پیکسلی نمایش دهید، بیت‌مپ برای هر یک از آن ۱۰۰۰۰۰۰ پیکسل به ۴ بایت حافظه نیاز دارد که در مجموع ۴ مگابایت حافظه می‌شود.

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

نظارت بر حافظه موجود و میزان استفاده از حافظه

قبل از اینکه بتوانید مشکلات مربوط به استفاده از حافظه برنامه خود را برطرف کنید، باید آنها را پیدا کنید. ابزار پروفایل حافظه اندروید استودیو به شما کمک می‌کند تا مشکلات حافظه را به روش‌های زیر پیدا و تشخیص دهید:

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

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

آزادسازی حافظه در پاسخ به رویدادها

همانطور که در بخش «مروری بر مدیریت حافظه» توضیح داده شده است، اندروید می‌تواند در صورت لزوم حافظه را از برنامه شما پس بگیرد یا برنامه شما را به طور کامل متوقف کند تا حافظه را برای وظایف حیاتی آزاد کند. برای کمک بیشتر به متعادل کردن حافظه سیستم و جلوگیری از نیاز سیستم به توقف فرآیند برنامه، می‌توانید رابط ComponentCallbacks2 را در کلاس‌های Activity خود پیاده‌سازی کنید. متد فراخوانی onTrimMemory() ارائه شده، برنامه شما را از رویدادهای چرخه عمر یا مرتبط با حافظه که فرصت خوبی برای برنامه شما فراهم می‌کنند تا به طور داوطلبانه استفاده از حافظه خود را کاهش دهد، مطلع می‌کند. آزاد کردن حافظه ممکن است تعداد دفعات از بین رفتن برنامه شما توسط قاتل کم حافظه را کاهش دهد.

پیاده‌سازی شما از onTrimMemory() باید منحصراً روی رویدادهای TRIM_MEMORY_UI_HIDDEN و TRIM_MEMORY_BACKGROUND متمرکز باشد. (از اندروید ۱۴ به بعد، سیستم دیگر برای سایر ثابت‌های قدیمی اعلان ارسال نمی‌کند. این ثابت‌ها رسماً در اندروید ۱۵ منسوخ شده‌اند.)

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

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

این نمونه کد نحوه پیاده‌سازی تابع فراخوانی onTrimMemory() را برای پاسخ به رویدادهای مختلف مرتبط با حافظه نشان می‌دهد:

کاتلین

import android.content.ComponentCallbacks2
// Other import statements.

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {

    // Other activity code.

    /**
     * Release memory when the UI becomes hidden or when system resources become low.
     * @param level the memory-related event that is raised.
     */
    override fun onTrimMemory(level: Int) {

        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // Release memory related to UI elements, such as bitmap caches.
        }

        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Release memory related to background processing, such as by
            // closing a database connection.
        }
    }
}

جاوا

import android.content.ComponentCallbacks2;
// Other import statements.

public class MainActivity extends AppCompatActivity
    implements ComponentCallbacks2 {

    // Other activity code.

    /**
     * Release memory when the UI becomes hidden or when system resources become low.
     * @param level the memory-related event that is raised.
     */
    public void onTrimMemory(int level) {

        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            // Release memory related to UI elements, such as bitmap caches.
        }

        if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
            // Release memory related to background processing, such as by
            // closing a database connection.
        }
    }
}

بررسی کنید که به چه مقدار حافظه نیاز دارید

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

برای جلوگیری از کمبود حافظه، می‌توانید از سیستم بخواهید میزان فضای هیپ موجود در دستگاه فعلی را تعیین کند. می‌توانید با فراخوانی getMemoryInfo() از سیستم این مقدار را درخواست کنید. این تابع یک شیء ActivityManager.MemoryInfo را برمی‌گرداند که اطلاعاتی در مورد وضعیت حافظه فعلی دستگاه، از جمله حافظه موجود، کل حافظه و آستانه حافظه - سطح حافظه‌ای که سیستم شروع به توقف فرآیندها می‌کند - ارائه می‌دهد. شیء ActivityManager.MemoryInfo همچنین lowMemory را نمایش می‌دهد که یک مقدار بولی ساده است و به شما می‌گوید که آیا دستگاه در حال کمبود حافظه است یا خیر.

قطعه کد مثال زیر نحوه استفاده از متد getMemoryInfo() را در برنامه شما نشان می‌دهد.

کاتلین

fun doSomethingMemoryIntensive() {

    // Before doing something that requires a lot of memory,
    // check whether the device is in a low memory state.
    if (!getAvailableMemory().lowMemory) {
        // Do memory intensive work.
    }
}

// Get a MemoryInfo object for the device's current memory status.
private fun getAvailableMemory(): ActivityManager.MemoryInfo {
    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    return ActivityManager.MemoryInfo().also { memoryInfo ->
        activityManager.getMemoryInfo(memoryInfo)
    }
}

جاوا

public void doSomethingMemoryIntensive() {

    // Before doing something that requires a lot of memory,
    // check whether the device is in a low memory state.
    ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();

    if (!memoryInfo.lowMemory) {
        // Do memory intensive work.
    }
}

// Get a MemoryInfo object for the device's current memory status.
private ActivityManager.MemoryInfo getAvailableMemory() {
    ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
    activityManager.getMemoryInfo(memoryInfo);
    return memoryInfo;
}

نظارت بر میزان خرابی حافظه کم

از کار افتادن حافظه کم (LMK) که توسط کاربر قابل مشاهده است، زمانی رخ می‌دهد که حافظه سیستم به شدت کم شود. وقتی حافظه کم است، lmkd (دیوی قاتل حافظه کم) فرآیندها را بر اساس oom_adj_score آنها خاتمه می‌دهد. برنامه‌هایی که در حافظه پنهان ذخیره شده‌اند یا سرویسی را بدون رابط کاربری مرتبط (مانند یک کار) اجرا می‌کنند، بالاترین امتیاز را دارند و ابتدا خاتمه می‌یابند. اگر حافظه به شدت کم باقی بماند، دیو مجبور می‌شود حافظه را از فرآیندهایی با oom_adj_score 0 بازپس بگیرد. از آنجا که این امتیاز برای برنامه‌های قابل مشاهده رزرو شده است، خاتمه آنها منجر به خروج فوری و نامناسب فرآیند می‌شود. برای کاربر نهایی، به نظر می‌رسد که برنامه از کار افتاده است، که اغلب مکانیسم‌های استاندارد ذخیره وضعیت چرخه عمر را دور می‌زند و منجر به از دست رفتن پیشرفت کاربر می‌شود.

حذف فرآیندهای پیش‌زمینه تمرکز اصلی در Android Vitals است زیرا آنها به عنوان یک پروکسی با دقت بالا برای سوء مدیریت حافظه عمل می‌کنند. در حالی که هر نرخ LMK بالاتر از 1٪ نشان دهنده نیاز مبرم به اقدام فوری است، نرخ پایین لزوماً نشانگر سلامت نیست. نرخ LMK پایین از نظر کاربر ممکن است به این معنی باشد که سرویس LMK مرتباً فرآیندهایی را که در پس‌زمینه هستند، حذف می‌کند، که این امر عملکرد "شروع گرم" و روان بودن چندوظیفگی را کاهش می‌دهد. بنابراین، توصیه می‌کنیم صرف نظر از امتیاز LMK فعلی خود، به بهترین شیوه‌های حافظه پایبند باشید تا از پایداری طولانی مدت و سلامت دستگاه اطمینان حاصل شود.

استفاده از ProfilingManager برای ردیابی مشکلات حافظه

پلتفرم اندروید ProfilingManager ارائه می‌دهد ، یک API پیشرفته برای مشاهده‌پذیری که به شما امکان می‌دهد داده‌های کاربر را در محیط تولید بر اساس triggerهایی که تنظیم می‌کنید، ثبت کنید. انجام این کار می‌تواند به شما در شناسایی مشکلات حافظه که به سختی قابل بازیابی هستند، کمک کند.

دو محرک جدید معرفی‌شده با اندروید ۱۷ به‌طور ویژه برای تشخیص مشکلات حافظه مفید هستند:

  • TRIGGER_TYPE_OOM نشان می‌دهد که برنامه خطای OutOfMemoryError را ایجاد کرده است. این خطا دفعه بعد که برنامه پس از خرابی شروع می‌شود، زمانی که برنامه برای پروفایلینگ تریگرها ثبت نام می‌کند، فعال می‌شود.
  • TRIGGER_TYPE_ANOMALY زمانی فعال می‌شود که سیستم رفتار غیرعادی را از برنامه تشخیص دهد. از جمله موارد دیگر، این می‌تواند به دلیل استفاده بیش از حد از حافظه فعال شود. این حالت پس از نمایش استفاده بیش از حد از حافظه توسط برنامه و قبل از اینکه سیستم اقدامی برای متوقف کردن فرآیند متخلف انجام دهد، فعال می‌شود. به عنوان مثال، اگر برنامه از محدودیت‌های حافظه معرفی شده در اندروید ۱۷ فراتر رود، TRIGGER_TYPE_ANOMALY قبل از اینکه سیستم برنامه را ببندد، فعال می‌شود.

برای اطلاعات بیشتر در مورد استفاده از ProfilingManager برای ثبت و بازیابی تریگرها به صورت برنامه‌نویسی، به مستندات پروفایلینگ مبتنی بر تریگر مراجعه کنید.

همچنین می‌توانید از پروفایلینگ مبتنی بر برنامه برای تعریف دستی نقاط شروع و پایان ردیابی استفاده کنید . توصیه می‌کنیم این کار را برای ثبت دستی heap dumpها یا heap profileها در مناطقی که مشکوک به نشت حافظه یا استفاده بیش از حد از حافظه هستید، انجام دهید.

از ساختارهای کد با بهره‌وری بیشتر در حافظه استفاده کنید

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

از خدمات به طور محدود استفاده کنید

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

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

به طور کلی، به دلیل فشار مداومی که سرویس‌های پایدار بر حافظه موجود وارد می‌کنند، از استفاده از آنها خودداری کنید. در عوض، توصیه می‌کنیم از یک پیاده‌سازی جایگزین مانند WorkManager استفاده کنید. برای اطلاعات بیشتر در مورد نحوه استفاده WorkManager برای زمان‌بندی فرآیندهای پس‌زمینه، به بخش Persistent work مراجعه کنید.

از کانتینرهای داده بهینه استفاده کنید

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

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

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

مراقب انتزاعات کد باشید

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

از protobuf های lite برای داده های سریالی استفاده کنید

بافرهای پروتکل (protobufs) یک مکانیزم توسعه‌پذیر، بی‌طرف از نظر زبان و پلتفرم هستند که توسط گوگل برای سریال‌سازی داده‌های ساختاریافته طراحی شده‌اند - مشابه XML، اما کوچک‌تر، سریع‌تر و ساده‌تر. اگر از protobufs برای داده‌های خود استفاده می‌کنید، همیشه از protobufs سبک در کد سمت کلاینت خود استفاده کنید. protobufs معمولی کد بسیار طولانی تولید می‌کنند که باعث افزایش فضای اشغال شده توسط کد برنامه شما در RAM می‌شود (به مدیریت و بهینه‌سازی فضای اشغال شده توسط کد برنامه خود مراجعه کنید) و به افزایش حجم APK کمک می‌کند.

برای اطلاعات بیشتر، به فایل readme مربوط به protobuf مراجعه کنید.

مراقب نشت حافظه باشید

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

برای اطلاعات بیشتر، به نشت حافظه مراجعه کنید.

از آشفتگی حافظه جلوگیری کنید

رویدادهای جمع‌آوری زباله (garbage collection) بر عملکرد برنامه شما تأثیری ندارند. با این حال، بسیاری از رویدادهای جمع‌آوری زباله که در مدت زمان کوتاهی رخ می‌دهند، می‌توانند به سرعت باتری را تخلیه کنند و همچنین به دلیل تعاملات لازم بین جمع‌آوری زباله و نخ‌های برنامه، زمان تنظیم فریم‌ها را به میزان کمی افزایش دهند. هرچه سیستم زمان بیشتری را صرف جمع‌آوری زباله کند، باتری سریع‌تر تخلیه می‌شود.

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

برای مثال، ممکن است چندین شیء موقت را درون یک حلقه for اختصاص دهید. یا ممکن است اشیاء Paint یا Bitmap جدیدی را درون تابع onDraw() یک view ایجاد کنید. در هر دو مورد، برنامه اشیاء زیادی را به سرعت و با حجم بالا ایجاد می‌کند. این اشیاء می‌توانند به سرعت تمام حافظه موجود در نسل جوان را مصرف کنند و باعث وقوع رویداد جمع‌آوری زباله شوند.

قبل از اینکه بتوانید آنها را اصلاح کنید، از Memory Profiler برای یافتن مکان‌هایی در کد خود که میزان ریزش حافظه در آنها زیاد است، استفاده کنید.

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

همچنین می‌توانید ارزیابی کنید که آیا object poolها برای مورد استفاده مفید هستند یا خیر. با object pool، به جای اینکه یک نمونه شیء را روی زمین بیندازید، آن را پس از اینکه دیگر نیازی به آن نیست، در یک pool رها می‌کنید. دفعه بعد که به یک نمونه شیء از آن نوع نیاز دارید، می‌توانید آن را از pool به دست آورید، نه اینکه آن را تخصیص دهید.

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

نگه داشتن نمونه‌های شیء بیشتر از حد نیاز در مخزن، بار سنگینی را بر دوش جمع‌آوری زباله (garbage collection) می‌گذارد. در حالی که مخازن شیء تعداد فراخوانی‌های جمع‌آوری زباله را کاهش می‌دهند، در نهایت میزان کار مورد نیاز برای هر فراخوانی را افزایش می‌دهند، زیرا این مقدار متناسب با تعداد بایت‌های زنده (قابل دسترسی) است.