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

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

مقدمه

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

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

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

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

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

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

بهترین شیوه‌ها برای برنامه‌های اندروید

بررسی کنید که تمام موارد زیر را در برنامه اندروید خود انجام داده‌اید:

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

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

منابع بلااستفاده را آزاد کنید

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

انتشار ارجاعات قدیمی 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
  }
}

لغو ثبت نام شنوندگان در صورت عدم نیاز

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

برای مثال، فرض کنید برنامه شما از 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()
}

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

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

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

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

اگر برنامه شما از SDK مربوط به Navigation یا Maps استفاده می‌کند، مطمئن شوید که منابع را با ارسال متدهای چرخه حیات (که با حروف پررنگ نشان داده شده‌اند) به 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()
  */
}

از آخرین نسخه‌های SDK استفاده کنید

کیت‌های توسعه نرم‌افزار گوگل (SDK) دائماً با ویژگی‌های جدید، رفع اشکالات و بهبود عملکرد به‌روزرسانی می‌شوند. برای دریافت این اصلاحات، SDKهای برنامه خود را به‌روز نگه دارید.

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

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

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

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

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

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

مرحله ۱: مسئله را دوباره طرح کنید

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

  • چه مجموعه ای از ویژگی ها فعال می شوند؟

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

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

    • آیا چندین بار تکرار را در حالت‌های مختلف چرخه حیات امتحان کرده‌اید؟

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

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

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

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

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

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

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

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

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

پروفایلر حافظه اندروید استودیو

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

شمارنده‌های حافظه پرفتو

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

Perfetto user interface

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

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

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

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

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

ردیابی تخصیص

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

مرحله ۴: میزان استفاده از حافظه برنامه خود را با استفاده از heap dump بررسی کنید

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

اندروید استودیو می‌تواند نشتی‌های حافظه‌ای را که توسط GC قابل رفع نیستند، تشخیص دهد. وقتی یک heap dump را ضبط می‌کنید، اندروید استودیو بررسی می‌کند که آیا activity یا fragment ای وجود دارد که هنوز قابل دسترسی باشد اما قبلاً از بین رفته باشد یا خیر.

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

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

ضبط یک هیپ دامپ

برای گرفتن یک هیپ دامپ، می‌توانید از Android Debug Bridge (adb) یا Android Studio Memory Profiler استفاده کنید.

استفاده از adb برای گرفتن یک هیپ دامپ

برای گرفتن یک heap dump با استفاده از adb ، این مراحل را دنبال کنید:

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

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

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

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

استفاده از اندروید استودیو برای گرفتن داده‌های هیپ دامپ

برای گرفتن یک heap dump با استفاده از Android Studio Memory Profiler، مراحل زیر را در بخش Android Capture a heapdump دنبال کنید.

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

پس از اینکه یک heap dump را ثبت کردید، می‌توانید از Memory Profiler اندروید استودیو برای تجزیه و تحلیل آن استفاده کنید. برای انجام این کار، این مراحل را دنبال کنید:

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

  2. گزینه Run را انتخاب کنید و سپس Debug configuration را انتخاب کنید.

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

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

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

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

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

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

    • ببینید هر شیء چقدر از حافظه را اشغال کرده است.

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

مرحله ۵: رفع نشت حافظه

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

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

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

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

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

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

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

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

نحوه گزارش مشکلات مربوط به SDK های گوگل

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

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

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

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

  • گزارش اشکالی که پس از بازسازی وضعیت نشتی توسط شما گرفته شده است.

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

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