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

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

מבוא

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

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

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

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

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

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

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

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

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

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

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

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

שחרור של הפניות לא תקינות של מפות Google ב-GeoSDKs

טעות נפוצה היא ש-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 והיא קורא ל-listener הבא כדי להאזין לאירועי הגעה:‏ addArrivalListener. אם האפליקציה קורא ל-method הזה כדי להאזין לאירועי הגעה, היא צריכה גם לקרוא ל-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 או ב-Navigation SDK. אפשר גם להשתמש ב-SupportNavigationFragment או ב-SupportMapFragment במקום להשתמש ישירות ב-NavigationView וב-MapView, בהתאמה. קטעי התמיכה מטפלים בהעברה של שיטות מחזור החיים.

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 באפליקציה.

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

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

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

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

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

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

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

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

  • איזו קבוצת תכונות מופעלת?

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

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

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

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

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

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

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

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

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

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

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

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

כלי לניתוח ביצועי הזיכרון ב-Android Studio

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. תיעוד תמונת מצב הזיכרון של המערכת
  2. ניתוח של גרסת ה-dump של אשכול כדי למצוא דליפות זיכרון
  3. תיקון דליפות זיכרון.

פרטים נוספים זמינים בסעיפים הבאים.

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

כדי לתעד dump של ערימה, אפשר להשתמש ב-Android Debug Bridge‏ (adb) או ב-Android Studio Memory Profiler.

שימוש ב-adb כדי לצלם תמונת מצב של אשכול

כדי לצלם גרסת dump של אשכול באמצעות adb:

  1. מחברים את מכשיר Android למחשב.
  2. פותחים שורת פקודה ועוברים לספרייה שבה נמצאים הכלים של adb.
  3. כדי לצלם גרסת dump של אשכול, מריצים את הפקודה הבאה :

    adb shell am dumpheap my.app.name $PHONE_FILE_OUT

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

    adb pull $PHONE_FILE_OUT $LOCAL_FILE.

שימוש ב-Android Studio כדי לתעד dump של ערימה

כדי לתעד dump ערימה באמצעות Memory Profiler של Android Studio, פועלים לפי השלבים המפורטים בקטע תיעוד heapdump ב-Android.

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

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

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

  2. בוחרים באפשרות Run (הפעלה) ואז באפשרות Debug (ניפוי באגים).

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

  4. בוחרים באפשרות זיכרון.

  5. בוחרים באפשרות Open heap dump ובוחרים את קובץ ה-heap dump שיצרתם. בפרופילר הזיכרון מוצג תרשים של השימוש בזיכרון של האפליקציה.

  6. משתמשים בתרשים כדי לנתח את האשפה:

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

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

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

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

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

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

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

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

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

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

מעקב אחר הקצאות הוא גם יתרון משמעותי, כי הוא מאפשר לכם לשתף את התוצאות בלי לכלול מידע רגיש שאפשר למצוא ב-heap.

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

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

איך מדווחים על בעיות ב-Google SDKs

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

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

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

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

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

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

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