روال‌های مطلوب مدیریت حافظه

این سند فرض می‌کند که شما راهنمای روال‌های مطلوب برای برنامه‌های Android را تحت مدیریت حافظه، مانند مدیریت حافظه برنامه، دنبال کرده‌اید.

مقدمه

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

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

قبل‌از تماس با پشتیبانی

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

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

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

روال‌های مطلوب برای برنامه‌های Android

بررسی کنید که همه موارد زیر را در برنامه Android خود انجام داده باشید:

  1. آزاد کردن منابع استفاده‌نشده.
  2. وقتی دیگر به شنوندگان نیاز ندارید، ثبت‌نام آن‌ها را لغو کنید.
  3. تکالیف را وقتی نیاز ندارید لغو کنید.
  4. روش‌های چرخه حیات را برای آزاد کردن منابع بازارسال کنید.
  5. از جدیدترین نسخه‌های کیت توسعه نرم‌افزار استفاده کنید.
  6. برای جلوگیری از خطاهای ANR، از مسدود کردن رشته اصلی درطول مقداردهی اولیه خودداری کنید.

برای جزئیات خاص هریک از این شیوه‌ها، بخش‌های زیر را ببینید.

آزاد کردن منابع استفاده‌نشده

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

انتشار ارجاع‌های قدیمی GoogleMap در GeoSDK

یک اشتباه رایج این است که GoogleMap می‌تواند باعث نشت حافظه شود اگر بااستفاده از NavigationView یا MapView ذخیره شده باشد. GoogleMap رابطه ۱ به ۱ با NavigationView یا MapView دارد که از آن بازیابی می‌شود. باید مطمئن شوید که GoogleMap در حافظه نهان ذخیره نشده است، یا اینکه وقتی NavigationView#onDestroy یا MapView#onDestroy فراخوانی می‌شود، مرجع آزاد می‌شود. اگر از NavigationSupportFragment،‏ MapSupportFragment، یا قطعه خودتان که این نماها را دربرمی‌گیرد استفاده می‌کنید، مرجع باید در Fragment#onDestroyView منتشر شود.

class NavFragment : SupportNavigationFragment() {

  var googleMap: GoogleMap?

  override fun onCreateView(
    inflater: LayoutInflater,
    parent: ViewGroup?,
    savedInstanceState: Bundle?,
  ): View  {
    super.onCreateView(inflater,parent,savedInstanceState)
    getMapAsync{map -> googleMap = map}
  }

  override fun onDestroyView() {
    googleMap = null
  }
}

وقتی دیگر به شنوندگان نیاز ندارید، ثبت‌نام آن‌ها را لغو کنید

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

برای مثال، فرض کنید برنامه شما از Navigation SDK استفاده می‌کند و شنونده زیر را برای گوش دادن به رویدادهای ورود فراخوانی می‌کند: addArrivalListener روش گوش دادن به رویدادهای ورود، همچنین باید removeArrivalListener را زمانی که دیگر نیازی به نظارت بر رویدادهای ورود ندارد فراخوانی کند.

var arrivalListener: Navigator.ArrivalListener? = null

fun registerNavigationListeners() {
  arrivalListener =
    Navigator.ArrivalListener {
      ...
    }
  navigator.addArrivalListener(arrivalListener)
}

override fun onDestroy() {
  navView.onDestroy()
  if (arrivalListener != null) {
    navigator.removeArrivalListener(arrivalListener)
  }

  ...
  super.onDestroy()
}

لغو کردن تکالیف درصورت عدم نیاز

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

برای جزئیات بیشتر درباره روال‌های مطلوب، به مدیریت حافظه برنامه در اسناد Android مراجعه کنید.

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

اگر برنامه‌تان از Navigation یا Maps SDK استفاده می‌کند، حتماً منابع را با بازارسال روش‌های چرخه حیات (به‌صورت پررنگ نشان داده شده است) به navView منتشر کنید. می‌توانید این کار را بااستفاده از NavigationView در Navigation SDK یا MapView در Maps یا Navigation SDK انجام دهید. همچنین می‌توانید به‌جای استفاده مستقیم از NavigationView و MapView، به‌ترتیب از SupportNavigationFragment یا SupportMapFragment استفاده کنید. تکه‌های پشتیبانی، بازارسال روش‌های چرخه حیات را مدیریت می‌کنند.

class NavViewActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    navView = ...
    navView.onCreate(savedInstanceState)
    ...
  }

  override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)
    navView.onSaveInstanceState(savedInstanceState)
  }

  override fun onTrimMemory(level: Int) {
    super.onTrimMemory(level)
    navView.onTrimMemory(level)
  }

  /* Same with
    override fun onStart()
    override fun onResume()
    override fun onPause()
    override fun onConfigurationChanged(...)
    override fun onStop()
    override fun onDestroy()
  */
}

استفاده از جدیدترین نسخه‌های کیت‌های توسعه نرم‌افزار

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

برای جلوگیری از خطاهای ANR، از مسدود کردن رشته اصلی درطول مقداردهی اولیه خودداری کنید

وقتی برنامه‌ای رشته اصلی را برای مدت طولانی مسدود می‌کند، می‌تواند باعث ایجاد خطای «برنامه پاسخ نمی‌دهد» (ANR) شود. برای جلوگیری از ANR، روش‌های چرخه حیات مثل onCreate() را با به‌تعویق انداختن کارهای طولانی‌مدت یا اجرای آن‌ها خارج از رشته اصلی، تا حد امکان سبک نگه دارید.

برای جلوگیری از ANR مربوط به مقداردهی اولیه کیت توسعه نرم‌افزار:

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

اشکال‌زدایی نشت حافظه

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

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

برای اشکال‌زدایی نشت حافظه، این فرایند را دنبال کنید:

  1. مشکل را دوباره ایجاد کنید. این مرحله برای اشکال‌زدایی آن ضروری است.
  2. بررسی کنید که آیا استفاده از حافظه مطابق انتظار است یا نه. بررسی کنید که افزایش مصرفی که به‌نظر می‌رسد نشت باشد، درواقع حافظه موردنیاز برای اجرای برنامه‌تان نباشد.
  3. اشکال‌زدایی در سطح بالا. چندین ابزار کمکی وجود دارد که می‌توانید برای اشکال‌زدایی استفاده کنید. سه مجموعه ابزار استاندارد مختلف به اشکال‌زدایی مشکلات حافظه در Android کمک می‌کنند: «استودیو Android»،‏ Perfetto، و ابزارهای کمکی خط فرمان «پل اشکال‌زدایی Android»‏ (adb).
  4. بررسی مصرف حافظه برنامه. رونوشت پشته و ردیابی تخصیص را دریافت کنید و سپس آن را تجزیه‌وتحلیل کنید.
  5. نشت حافظه را برطرف کنید.

بخش‌های زیر این مراحل را به‌طور مفصل پوشش می‌دهند.

مرحله ۱: مشکل را دوباره ایجاد کنید

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

  • کدام مجموعه از ویژگی‌ها فعال شده است؟

  • چه توالی خاصی از اقدامات کاربر باعث نشت می‌شود؟

    • آیا چندین بار برای فعال کردن این توالی تلاش کرده‌اید؟
  • برنامه کدام حالت‌های چرخه حیات را طی کرده است؟

    • آیا تکرارهای متعدد را ازطریق حالت‌های مختلف چرخه حیات امتحان کرده‌اید؟

مطمئن شوید که می‌توانید مشکل را در جدیدترین نسخه کیت‌های توسعه نرم‌افزار بازآفرینی کنید. ممکن است مشکل نسخه قبلی قبلاً برطرف شده باشد.

مرحله ۲: بررسی کنید آیا استفاده از حافظه برای برنامه موردانتظار است یا نه

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

  • احتمالاً نشت: فعال کردن سناریو ازطریق تکرارهای متعدد منجر به افزایش استفاده از حافظه درطول زمان می‌شود.

  • استفاده از حافظه موردانتظار: حافظه پس‌از توقف سناریو بازیابی می‌شود.

  • استفاده از حافظه احتمالاً موردانتظار: استفاده از حافظه برای مدتی افزایش می‌یابد و سپس کاهش می‌یابد. این ممکن است به‌دلیل حافظه پنهان محدود یا استفاده موردانتظار دیگری از حافظه باشد.

اگر رفتار برنامه احتمالاً استفاده موردانتظار از حافظه است، این مشکل را می‌توان با مدیریت حافظه برنامه برطرف کرد. برای دریافت راهنمایی، مدیریت حافظه برنامه را ببینید.

مرحله ۳: اشکال‌زدایی در سطح بالا

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

تحلیل‌گر حافظه «استودیو Android»

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

شمارنده‌های حافظه Perfetto

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

میانای کاربر Perfetto

ابزارهای کمکی خط فرمان پل اشکال‌زدایی Android‏ (adb)

بسیاری از چیزهایی که می‌توانید با Perfetto ردیابی کنید به‌عنوان ابزار کمکی خط فرمان adb نیز دردسترس است که می‌توانید مستقیماً از آن پُرسمان کنید. چند نمونه مهم عبارت‌اند از:

  • Meminfo به شما امکان می‌دهد اطلاعات دقیق حافظه را در یک نقطه زمانی ببینید.

  • Procstats برخی‌از آمارهای مهم انبوهشی را درطول زمان ارائه می‌دهد.

آمار مهمی که باید در اینجا بررسی شود، حداکثر ردپای حافظه فیزیکی (maxRSS) است که برنامه درطول زمان نیاز دارد. ممکن است MaxPSS چندان دقیق نباشد. برای آشنایی با روشی برای افزایش دقت، پرچم adb shell dumpsys procstats --help –start-testing را ببینید.

ردگیری تخصیص

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

مرحله ۴: بررسی مصرف حافظه برنامه با رونوشت پشته

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

Android Studio می‌تواند نشت‌های حافظه‌ای را که GC نمی‌تواند برطرف کند شناسایی کند. وقتی رونوشت پشته‌ای را ضبط می‌کنید، Android Studio بررسی می‌کند که آیا فعالیت یا قطعه‌ای وجود دارد که هنوز قابل‌دسترسی باشد اما قبلاً ازبین رفته باشد.

  1. رونوشت پشته را ضبط کنید.
  2. رونوشت پشته را برای پیدا کردن نشت حافظه تجزیه‌وتحلیل کنید.
  3. رفع نشت حافظه.

برای جزئیات، بخش‌های زیر را ببینید.

ضبط رونوشت پشته

برای ضبط رونوشت پشته، می‌توانید از «پل اشکال‌زدایی Android»‏ (adb) یا «تحلیل‌گر حافظه استودیو Android» استفاده کنید.

از adb برای ضبط رونوشت پشته استفاده کنید

برای ضبط رونوشت پشته بااستفاده از adb، این مراحل را دنبال کنید:

  1. دستگاه Android را به رایانه وصل کنید.
  2. خط فرمان را باز کنید و به دایرکتوری‌ای که ابزارهای adb در آن قرار دارد بروید.
  3. برای ضبط رونوشت پشته، این فرمان را اجرا کنید :

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

  4. برای بازیابی رونوشت پشته، این فرمان را اجرا کنید:

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

استفاده از «استودیو Android» برای ضبط رونوشت پشته

برای ضبط رونوشت پشته بااستفاده از «تحلیل‌گر حافظه استودیو Android»، این مراحل را در بخش ضبط رونوشت پشته Android دنبال کنید.

رونوشت پشته را برای پیدا کردن نشت‌های حافظه تحلیل کنید

پس‌از ضبط رونوشت پشته، می‌توانید از «تحلیل‌گر حافظه» در «استودیو Android» برای تحلیل آن استفاده کنید. برای انجام این کار، این مراحل را دنبال کنید:

  1. پروژه Android خود را در «استودیو Android» باز کنید.

  2. اجرا و سپس پیکربندی اشکال‌زدایی را انتخاب کنید.

  3. برگه Android Profiler را باز کنید.

  4. حافظه را انتخاب کنید.

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

  6. از نمودار برای تجزیه‌وتحلیل رونوشت پشته استفاده کنید:

    • اشیاء را که دیگر استفاده نمی‌شوند شناسایی کنید.

    • اشیاء استفاده‌کننده از حافظه زیاد را شناسایی کنید.

    • ببینید هر شیء چقدر حافظه مصرف می‌کند.

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

مرحله ۵: برطرف کردن نشت حافظه

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

سایر ابزارهای اشکال‌زدایی

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

اشکال‌زدایی حافظه در کد بومی با ردیابی تخصیص

حتی اگر مستقیماً از کد بومی استفاده نمی‌کنید، چندین کتابخانه رایج Android ازجمله کیت‌های توسعه نرم‌افزار Google از آن استفاده می‌کنند. اگر فکر می‌کنید نشت حافظه در کد بومی است، چندین ابزار وجود دارد که می‌توانید برای اشکال‌زدایی آن استفاده کنید. ردیابی تخصیص با استودیو Android یا heapprofd (که با Perfetto نیز سازگار است) روشی عالی برای شناسایی علل احتمالی نشت حافظه است و اغلب سریع‌ترین روش برای اشکال‌زدایی است.

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

شناسایی نشت‌ها با LeakCanary

‫LeakCanary ابزاری قدرتمند برای شناسایی نشت حافظه در برنامه‌های Android است. برای کسب اطلاعات بیشتر درباره نحوه استفاده از LeakCanary در برنامه‌تان، به LeakCanary مراجعه کنید.

نحوه گزارش کردن مشکلات مربوط به کیت‌های توسعه نرم‌افزار Google

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

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

  • رونوشت‌های پشته ضبط‌شده از برنامه‌تان با مشکل بازسازی‌شده. رونوشت‌های پشته را در دو زمان مختلف ضبط کنید که نشان دهد استفاده از حافظه به‌میزان قابل‌توجهی افزایش یافته است.

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

  • گزارش اشکال که پس‌از بازآفرینی شرایط نشت گرفته شده باشد.

  • رد پشته‌ای هرگونه خرابی مربوط به حافظه.

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