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

המסמך הזה מבוסס על ההנחה שפעלתם לפי השיטות המומלצות לניהול הזיכרון באפליקציות ל-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#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, בהתאמה. ה-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.

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

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

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