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

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

מבוא

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

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

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

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

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

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

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

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

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

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

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

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

פרסום הפניות לא עדכניות של Google Map ב-GeoSDK

טעות נפוצה היא שמפת Google Map עלולה לגרום לדליפת זיכרון אם היא שמורה במטמון באמצעות NavigationView או MapView. ל-Google Map יש קשר 1-1 עם ה-NavigationView או ה-MapView שממנו היא מאוחזרת. עליכם לוודא ש-Google Map לא נשמר במטמון או שההפניה משוחררת בזמן קריאה ל-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 רושמת האזנה לאירוע, כמו לחיצה על לחצן או שינוי במצב תצוגה, עליך לבטל את הרישום של ה-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()
}

איך מבטלים משימות כשלא צריך אותן

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

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

העברת שיטות של מחזור החיים לשחרור משאבים

אם באפליקציה נעשה שימוש ב-SDK של הניווט או של מפות Google, חשוב לשחרר את המשאבים על ידי העברת שיטות של מחזור החיים (מוצגות בגופן מודגש) אל navView. אפשר לעשות זאת באמצעות NavigationView ב-API של הניווט או באמצעות MapView ב-'מפות' או ב-'ניווט 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

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

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

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

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

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

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

בחלקים הבאים נפרט את השלבים האלה.

שלב 1: יוצרים מחדש את הבעיה

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

מוני זיכרון פרפטו

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

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

כלי עזר לשורת הפקודה של Android (adb)

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

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

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

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

מעקב הקצאה

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

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

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

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

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

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

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

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

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

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

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

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

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

  2. לוחצים על הפעלה ואז בוחרים בהגדרה ניפוי באגים.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

LeakCanary הוא כלי מתקדם לזיהוי דליפות זיכרון באפליקציות ל-Android. ב-LeakCanary מוסבר איך משתמשים ב-LeakCanary באפליקציה.

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

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

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

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

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

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

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

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