שיטות מומלצות לניהול זיכרון

במסמך הזה אנחנו יוצאים מנקודת הנחה שפעלתם לפי ההנחיות לשיטות מומלצות לאפליקציות ל-Android בקטע "ניהול זיכרון", כמו ניהול הזיכרון של האפליקציה.

מבוא

דליפת זיכרון היא סוג של דליפת משאבים שמתרחשת כשתוכנת מחשב לא משחררת זיכרון שהוקצה לה וכבר לא נחוץ לה. זליגת זיכרון יכולה לגרום לאפליקציה לבקש ממערכת ההפעלה יותר זיכרון ממה שיש לה, וכתוצאה מכך האפליקציה עלולה לקרוס. יש כמה שיטות לא תקינות שיכולות לגרום לדליפות זיכרון באפליקציות ל-Android, כמו אי-סילוק משאבים בצורה תקינה או אי-ביטול הרישום של מאזינים כשאין בהם יותר צורך.

במאמר הזה נסביר על כמה שיטות מומלצות שיעזרו לכם למנוע, לזהות ולפתור דליפות זיכרון בקוד. אם ניסיתם את השיטות שמתוארות במסמך הזה ואתם חושדים שיש דליפת זיכרון בערכות ה-SDK שלנו, קראו את המאמר איך מדווחים על בעיות בערכות ה-SDK של Google.

לפני שפונים לתמיכה

לפני שאתם מדווחים על דליפת זיכרון לצוות התמיכה של Google, כדאי לפעול לפי השיטות המומלצות ושלבי ניפוי הבאגים שמפורטים במסמך הזה כדי לוודא שהשגיאה לא נמצאת בקוד שלכם. יכול להיות שהשלבים האלה יפתרו את הבעיה, ואם לא, הם יפיקו את המידע שצוות התמיכה של Google צריך כדי לעזור לכם.

מניעת דליפות זיכרון

כדי להימנע מכמה מהגורמים הנפוצים ביותר לדליפות זיכרון בקוד שמשתמש בערכות ה-SDK של Google, כדאי לפעול לפי השיטות המומלצות הבאות.

שיטות מומלצות לאפליקציות ל-Android

מוודאים שביצעתם את כל הפעולות הבאות באפליקציה ל-Android:

  1. שחרור משאבים שלא בשימוש.
  2. לבטל את הרישום של מאזינים כשאין בהם צורך יותר.
  3. ביטול משימות כשאין בהן צורך.
  4. העברת שיטות של מחזור חיים כדי לשחרר משאבים.
  5. משתמשים בגרסאות העדכניות ביותר של ערכות ה-SDK.
  6. כדי למנוע שגיאות ANR, אל תחסמו את ה-thread הראשי במהלך ההפעלה.

בקטעים הבאים מפורטים פרטים ספציפיים לגבי כל אחת מהשיטות האלה.

שחרור משאבים שלא בשימוש

כשמשתמשים במשאב באפליקציה ל-Android, חשוב לשחרר את המשאב כשלא צריך אותו יותר. אם לא תעשו את זה, המשאב ימשיך לתפוס זיכרון גם אחרי שהאפליקציה תסיים להשתמש בו. מידע נוסף זמין בקטע מחזור החיים של פעילות במסמכי התיעוד של Android.

שחרור הפניות לא עדכניות ל-GoogleMap ב-GeoSDK

טעות נפוצה היא ש-GoogleMap יכול לגרום לדליפת זיכרון אם הוא נשמר במטמון באמצעות NavigationView או MapView. ל-GoogleMap יש קשר של 1 ל-1 עם NavigationView או MapView שממנו הוא מאוחזר. צריך לוודא ש-GoogleMap לא נשמר במטמון, או שההפניה משוחררת כשמתבצעת קריאה ל-NavigationView#onDestroy או ל-MapView#onDestroy. אם משתמשים ב-NavigationSupportFragment, ב-MapSupportFragment או ב-fragment משלכם שעוטף את התצוגות האלה, צריך לשחרר את ההפניה ב-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 SDK או ב-Maps SDK, חשוב לשחרר את המשאבים על ידי העברת שיטות של מחזור חיים (מודגשות) אל navView. אפשר לעשות את זה באמצעות NavigationView ב-Navigation SDK או באמצעות MapView ב-Maps SDK או ב-Navigation SDK. אפשר גם להשתמש ב-SupportNavigationFragment או ב-SupportMapFragment במקום להשתמש ישירות ב-NavigationView וב-MapView, בהתאמה. ה-support fragments מטפלים בהעברה של השיטות של מחזור החיים.

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 של Google מתעדכנות כל הזמן עם תכונות חדשות, תיקוני באגים ושיפורים בביצועים. כדי לקבל את התיקונים האלה, חשוב לעדכן את ערכות ה-SDK באפליקציה.

כדי למנוע שגיאות ANR, מומלץ להימנע מחסימה של ה-thread הראשי במהלך ההפעלה

כשאפליקציה חוסמת את ה-thread הראשי למשך זמן רב מדי, יכולה להופיע השגיאה "האפליקציה לא מגיבה" (ANR). כדי למנוע שגיאות ANR, צריך לשמור על שיטות מחזור חיים כמו onCreate() קלות ככל האפשר, על ידי דחיית משימות שפועלות לאורך זמן או הפעלתן מחוץ ל-thread הראשי.

כדי למנוע מקרי ANR שקשורים לאתחול של ה-SDK:

  • צריך ליצור רק מופע אחד של מפה בכל פעם.
  • צמצמו ככל האפשר את העבודה בשרשור ה-UI בזמן יצירת המופע של המפה.

ניפוי באגים של דליפות זיכרון

אם אתם עדיין רואים דליפות זיכרון אחרי שיישמתם את כל ההצעות הרלוונטיות שמופיעות למעלה, בצעו את התהליך הבא כדי לנפות באגים.

לפני שמתחילים, חשוב להכיר את האופן שבו Android מנהלת את הזיכרון. למידע נוסף, קראו את הסקירה הכללית של ניהול הזיכרון ב-Android.

כדי לנפות באגים של דליפות זיכרון, פועלים לפי התהליך הבא:

  1. משחזרים את הבעיה. השלב הזה חיוני לניפוי באגים.
  2. בודקים אם השימוש בזיכרון הוא כמצופה. חשוב לוודא שהעלייה בשימוש שנראית כמו דליפה היא לא בעצם הזיכרון שנדרש להפעלת האפליקציה.
  3. ניפוי באגים ברמה גבוהה. יש כמה כלים שאפשר להשתמש בהם לניפוי באגים. יש שלוש קבוצות שונות של כלים סטנדרטיים לניפוי באגים בבעיות שקשורות לזיכרון ב-Android: ‏Android Studio, ‏Perfetto וכלי שורת הפקודה של Android Debug Bridge (adb).
  4. בודקים את השימוש בזיכרון של האפליקציה. יוצרים תמונת מצב של הזיכרון ומעקב אחרי הקצאות, ואז מנתחים אותם.
  5. תיקון דליפות זיכרון.

בקטעים הבאים מוסבר בפירוט על השלבים האלה.

שלב 1: משחזרים את הבעיה

אם לא הצלחתם לשחזר את הבעיה, קודם כל חשבו על התרחישים שיכולים להוביל לדליפת הזיכרון. אם אתם יודעים שהבעיה שוחזרה, אתם יכולים לעבור ישירות לתמונת המצב של הזיכרון. עם זאת, אם אתם יוצרים תמונת מצב של הזיכרון רק כשהאפליקציה מופעלת או בנקודת זמן אקראית אחרת, יכול להיות שלא הפעלתם את התנאים שגורמים לדליפה. כדאי לנסות תרחישים שונים כשמנסים לשחזר את הבעיה:

  • אילו תכונות מופעלות?

  • איזה רצף ספציפי של פעולות משתמש גורם לדליפה?

    • ניסית להפעיל את הרצף הזה כמה פעמים?
  • אילו מצבים של מחזור חיים האפליקציה עברה?

    • האם ניסיתם כמה איטרציות במצבים שונים של מחזור החיים?

חשוב לוודא שאפשר לשחזר את הבעיה בגרסה העדכנית של ערכות ה-SDK. יכול להיות שהבעיה בגרסה הקודמת כבר תוקנה.

שלב 2: בודקים אם השימוש בזיכרון של האפליקציה הוא כמצופה

כל תכונה דורשת זיכרון נוסף. כשמנפים באגים בתרחישים שונים, חשוב לבדוק אם מדובר בשימוש צפוי או בדליפת זיכרון. לדוגמה, לתכונות שונות או למשימות שונות של משתמשים, כדאי לשקול את האפשרויות הבאות:

  • כנראה שיש דליפה: הפעלת התרחיש בכמה חזרות גורמת לעלייה בשימוש בזיכרון לאורך זמן.

  • השימוש הצפוי בזיכרון: הזיכרון מתפנה אחרי שהתרחיש נעצר.

  • שימוש צפוי בזיכרון: השימוש בזיכרון עולה לפרק זמן מסוים ואז יורד. יכול להיות שהסיבה לכך היא מטמון מוגבל או שימוש צפוי אחר בזיכרון.

אם התנהגות האפליקציה היא שימוש צפוי בזיכרון, אפשר לטפל בבעיה על ידי ניהול הזיכרון של האפליקציה. כאן מוסבר איך לנהל את הזיכרון של האפליקציה.

שלב 3: ניפוי באגים ברמה גבוהה

כשמנפים באגים של דליפת זיכרון, מתחילים ברמה גבוהה, ואז מתעמקים בפרטים אחרי שמצמצמים את האפשרויות. כדי לבדוק אם יש זליגה לאורך זמן, אפשר להשתמש באחד מכלי ניפוי הבאגים הבאים ברמה גבוהה:

‫Memory Profiler ב-Android Studio

הכלי הזה מספק היסטוגרמה חזותית של הזיכרון שנצרך. אפשר גם להפעיל תמונות מצב של הזיכרון ומעקב אחרי הקצאות מאותו ממשק. הכלי הזה הוא ההמלצה שמוגדרת כברירת מחדל. מידע נוסף זמין במאמר Android Studio Memory Profiler.

מוני זיכרון של Perfetto

Perfetto מאפשר לכם לשלוט באופן מדויק במעקב אחרי כמה מדדים, ומציג את כולם בהיסטוגרמה אחת. מידע נוסף זמין במאמר Perfetto Memory Counters.

ממשק המשתמש של Perfetto

כלי שורת הפקודה של Android Debug Bridge‏ (ADB)

רוב הדברים שאפשר לעקוב אחריהם באמצעות Perfetto זמינים גם ככלי שורת פקודה של adb, שאפשר לשלוח אליו שאילתות ישירות. הנה כמה דוגמאות חשובות:

  • Meminfo מאפשר לראות מידע מפורט על הזיכרון בנקודת זמן מסוימת.

  • Procstats מספק נתונים סטטיסטיים מצטברים חשובים לאורך זמן.

נתון סטטיסטי חשוב שצריך לבדוק הוא הזיכרון שבשימוש הפיזי המקסימלי שהאפליקציה צריכה לאורך זמן (maxRSS). יכול להיות שהערך של MaxPSS לא יהיה מדויק. כדי לשפר את הדיוק, אפשר להשתמש בדגל adb shell dumpsys procstats --help –start-testing.

מעקב אחרי הקצאות

מעקב אחרי הקצאות מזהה את דוח הקריסות שבו הוקצה זיכרון, ואם הוא לא התפנה. השלב הזה שימושי במיוחד כשמאתרים דליפות בקוד Native. הכלי הזה מזהה את דוח הקריסות, ולכן הוא יכול להיות אמצעי מצוין לניפוי באגים מהיר של שורש הבעיה או להבין איך לשחזר את הבעיה. לשלבים לשימוש במעקב אחר הקצאות, ראו ניפוי באגים בזיכרון בקוד Native באמצעות מעקב אחר הקצאות.

שלב 4: בודקים את השימוש בזיכרון של האפליקציה באמצעות תמונת מצב של הזיכרון

אחת הדרכים לזהות דליפת זיכרון היא ליצור תמונת מצב של הזיכרון באפליקציה ולבדוק אם יש בה דליפות. תמונת מצב של הזיכרון היא תמונת מצב של כל האובייקטים בזיכרון של האפליקציה. אפשר להשתמש בו כדי לאבחן דליפות זיכרון ובעיות אחרות שקשורות לזיכרון.

‫Android Studio יכול לזהות דליפות זיכרון שאי אפשר לתקן באמצעות ה-GC. כשיוצרים תמונת מצב של הזיכרון, Android Studio בודק אם יש פעילות או מקטע שעדיין אפשר להגיע אליהם אבל הם כבר נהרסו.

  1. יצירת תמונת מצב של הזיכרון.
  2. מנתחים את תמונת המצב של הזיכרון כדי למצוא דליפות זיכרון.
  3. תיקון דליפות זיכרון.

בסעיפים הבאים יש מידע נוסף בנושא.

צילום של תמונת מצב של הזיכרון

כדי לתעד תמונת מצב של הזיכרון, אתם יכולים להשתמש ב-Android Debug Bridge‏ (adb) או ב-Memory Profiler ב-Android Studio.

שימוש ב-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 Studio כדי ליצור תמונת מצב של הזיכרון

כדי לתעד תמונת מצב של הזיכרון באמצעות Memory Profiler ב-Android Studio, פועלים לפי השלבים בקטע Capture a heapdump במדריך של Android.

ניתוח תמונת המצב של הזיכרון כדי למצוא דליפות זיכרון

אחרי שיוצרים תמונת מצב של הזיכרון, אפשר להשתמש ב-Memory Profiler של Android Studio כדי לנתח אותה. לשם כך, בצע את הצעדים הבאים:

  1. פותחים את פרויקט Android ב-Android Studio.

  2. לוחצים על Run ואז על ההגדרה Debug.

  3. פותחים את הכרטיסייה Android Profiler.

  4. לוחצים על זיכרון.

  5. בוחרים באפשרות פתיחת תמונת מצב של הזיכרון ובוחרים את קובץ תמונת המצב של הזיכרון שיצרתם. הכלי Memory Profiler מציג תרשים של השימוש בזיכרון של האפליקציה.

  6. משתמשים בתרשים כדי לנתח את תמונת המצב של הזיכרון:

    • מזהים אובייקטים שכבר לא בשימוש.

    • לזהות אובייקטים שמשתמשים בהרבה זיכרון.

    • כמה זיכרון כל אובייקט צורך.

  7. אתם יכולים להשתמש במידע הזה כדי לצמצם את האפשרויות או למצוא את המקור של דליפת הזיכרון ולתקן אותה.

שלב 5: תיקון דליפות זיכרון

אחרי שמזהים את המקור של דליפת הזיכרון, אפשר לתקן אותה. תיקון דליפות זיכרון באפליקציות ל-Android עוזר לשפר את הביצועים והיציבות של האפליקציות. הפרטים משתנים בהתאם לתרחיש. עם זאת, ההצעות הבאות יכולות לעזור:

כלים אחרים לניפוי באגים

אם ביצעתם את השלבים האלה ועדיין לא מצאתם ותיקנתם את דליפת הזיכרון, נסו את הכלים הבאים:

ניפוי באגים בזיכרון בקוד Native באמצעות מעקב אחר הקצאות

גם אם אתם לא משתמשים ישירות בקוד Native, כמה ספריות נפוצות של Android משתמשות בו, כולל ערכות ה-SDK של Google. אם אתם חושבים שדליפת הזיכרון היא בקוד Native, יש כמה כלים שאתם יכולים להשתמש בהם כדי לנפות באגים. מעקב אחרי הקצאות באמצעות Android Studio או heapprofd (תואם גם ל-Perfetto) הוא דרך מצוינת לזהות גורמים פוטנציאליים לדליפת זיכרון, ולרוב זו הדרך המהירה ביותר לנפות באגים.

יתרון נוסף של מעקב אחרי הקצאות הוא שאפשר לשתף את התוצאות בלי לכלול מידע רגיש שיכול להימצא בערימה.

זיהוי דליפות באמצעות LeakCanary

‫LeakCanary הוא כלי יעיל לזיהוי דליפות זיכרון באפליקציות ל-Android. מידע נוסף על השימוש ב-LeakCanary באפליקציה זמין ב-LeakCanary.

איך לדווח על בעיות בערכות ה-SDK של Google

אם ניסיתם את השיטות שמופיעות במסמך הזה ואתם חושדים שיש דליפת זיכרון בערכות ה-SDK שלנו, פנו לתמיכת הלקוחות וספקו כמה שיותר מהפרטים הבאים:

  • השלבים לשחזור דליפת הזיכרון. אם השלבים דורשים קידוד מורכב, יכול להיות שכדאי להעתיק את הקוד שמשחזר את הבעיה לאפליקציה לדוגמה שלנו ולספק שלבים נוספים שצריך לבצע בממשק המשתמש כדי להפעיל את הזליגה.

  • תמונות מצב של הזיכרון שנוצרו מהאפליקציה שלכם עם הבעיה ששוחזרה. צלמו תמונות מצב של הזיכרון בשתי נקודות זמן שונות, שמראות שהשימוש בזיכרון גדל באופן משמעותי.

  • אם צפויה דליפת זיכרון מקומית, משתפים את פלט מעקב ההקצאה מ-heapprofd.

  • דוח על באג שנוצר אחרי ששחזרתם את מצב הזליגה.

  • דוחות קריסות שקשורות לזיכרון.

    הערה חשובה: בדרך כלל דוחות קריסות לא מספיקים כדי לנפות באגים בבעיות בזיכרון, לכן חשוב לספק גם אחד מסוגי המידע האחרים.