במסמך הזה אנחנו יוצאים מנקודת הנחה שביצעתם את ההנחיות לשיטות מומלצות לאפליקציות ל-Android בנושא ניהול זיכרון, כמו ניהול הזיכרון של האפליקציה.
מבוא
דליפת זיכרון היא סוג של דליפת משאבים שמתרחשת כשתוכנת מחשב לא משחררת זיכרון שהוקצה ולא נדרש יותר. דליפה יכולה לגרום לאפליקציה לבקש יותר זיכרון ממערכת ההפעלה מהזיכרון שיש לה, וכתוצאה מכך לקריסה של האפליקציה. יש כמה שיטות לא נכונות שעלולות לגרום לדליפות זיכרון באפליקציות ל-Android, למשל אי-ניפוי משאבים כראוי או אי-ביטול הרישום של מאזינים כשאין בהם יותר צורך.
במסמך הזה מפורטות כמה שיטות מומלצות למניעת דליפות זיכרון בקוד, לאיתור ולפתרון שלהן. אם ניסיתם את השיטות שמפורטות במסמך הזה וחושדים שיש דליפת זיכרון ב-SDK שלנו, תוכלו לעיין במאמר איך מדווחים על בעיות ב-Google SDK.
לפני שפונים לתמיכה
לפני שמדווחים על דליפת זיכרון לצוות התמיכה של Google, צריך לפעול לפי השיטות המומלצות ולבצע את שלבי ניפוי הבאגים שמפורטים במסמך הזה כדי לוודא שהשגיאה לא נמצאת בקוד. הפעולות האלה יכולות לפתור את הבעיה, ואם לא, נוצר המידע הדרוש לצוות התמיכה של Google כדי לעזור לכם.
מניעת דליפות זיכרון
כדי להימנע מכמה מהסיבות הנפוצות ביותר לזיהום זיכרון בקוד שמשתמש ב-Google SDKs, כדאי לפעול לפי השיטות המומלצות הבאות.
שיטות מומלצות לאפליקציות ל-Android
בודקים שביצעת את כל הפעולות הבאות באפליקציה ל-Android:
- משחררים משאבים שלא בשימוש.
- להסיר את הרישום של המאזינים כשאין בהם צורך יותר.
- איך מבטלים משימות אם לא צריך
- העברת שיטות של מחזור חיים כדי לשחרר משאבים
- שימוש בגרסאות האחרונות של ערכות ה-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 SDK או ב-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.
כדי לנפות באגים של דליפות זיכרון, פועלים לפי התהליך הבא:
- משחזרים את הבעיה. השלב הזה חיוני לניפוי הבאגים.
- בודקים אם השימוש בזיכרון צפוי. צריך לבדוק אם השימוש המוגבר שנראה כדליפה הוא למעשה הזיכרון הנדרש להפעלת האפליקציה.
- ניפוי באגים ברמה גבוהה. יש כמה כלי עזר שאפשר להשתמש בהם לניפוי באגים. יש שלוש קבוצות סטנדרטיות של כלים שיעזרו לכם לנפות באגים בבעיות שקשורות לזיכרון ב-Android: Android Studio, Perfetto ו-Android Debug Bridge (adb) – כלי שורת הפקודה.
- בדיקת השימוש בזיכרון של האפליקציה. איך מקבלים גרסת dump של אשכול ומעקב אחר הקצאות, ואז מנתחים את הנתונים.
- פתרון דליפות זיכרון
הקטעים הבאים מתארים את השלבים האלה בפירוט.
שלב 1: משחזרים את הבעיה
אם לא הצלחתם ליצור מחדש את הבעיה, כדאי קודם לבדוק את התרחישים שיכולים להוביל לדליפת הזיכרון. אם אתם יודעים שהבעיה נוצרה מחדש, תוכלו לעבור ישירות לבדיקה של גרסת heap dump. עם זאת, אם מופיעה רק תמונת מצב של הזיכרון בזמן ההפעלה של האפליקציה או בשלב אקראי אחר, יכול להיות שלא הפעלתם את התנאים כדי לגרום לדליפה. כדאי לנסות לשחזר את הבעיה בכמה תרחישים שונים:
איזו קבוצת תכונות מופעלת?
איזה רצף ספציפי של פעולות משתמשים מפעיל את הדליפה?
- ניסית להפעיל את התהליך הזה כמה פעמים?
באילו מצבים במחזור החיים האפליקציה עברה?
- ניסית כמה חזרות דרך מצבים שונים במחזור החיים?
מוודאים שאפשר ליצור מחדש את הבעיה בגרסה האחרונה של ערכות ה-SDK. יכול להיות שהבעיה מגרסה קודמת כבר נפתרה.
שלב 2: בודקים אם האפליקציה צפויה להשתמש בזיכרון
כל תכונה דורשת זיכרון נוסף. כשמנסים לנפות באגים בתרחישים שונים, חשוב לבדוק אם מדובר בשימוש צפוי או אם מדובר בדליפה של זיכרון. לדוגמה, לתכונות או למשימות שונות של משתמשים, כדאי לשקול את האפשרויות הבאות:
סביר להניח שזו דליפת זיכרון: הפעלת התרחיש באמצעות מספר חזרות גורמת לעלייה בנפח השימוש בזיכרון לאורך זמן.
שימוש צפוי בזיכרון: הזיכרון מוחזר לאחר שהתרחיש מושהה.
שימוש צפוי בזיכרון: השימוש בזיכרון גדל לפרק זמן מסוים ולאחר מכן חוזר ונשנה יותר. הסיבה לכך יכולה להיות שימוש במטמון מוגבל או שימוש צפוי אחר בזיכרון.
אם סביר להניח שהתנהגות האפליקציה היא שימוש צפוי בזיכרון, אפשר לטפל בבעיה על ידי ניהול הזיכרון של האפליקציה. במאמר ניהול הזיכרון של האפליקציה מוסבר איך לעשות זאת.
שלב 3: ניפוי באגים ברמה גבוהה
כשמנסים לנפות באגים של דליפת זיכרון, מתחילים ברמה גבוהה ולאחר מכן מתמקדים ברמה נמוכה יותר אחרי שמצמצמים את האפשרויות. כדי לבדוק אם יש דליפת נתונים לאורך זמן, תוכלו להשתמש באחד מכלי ניפוי הבאגים ברמה גבוהה:
Android Studio Memory Profiler (מומלץ)
כלי לניתוח ביצועי הזיכרון ב-Android Studio
הכלי הזה מציג תרשים עמודות חזותי של נפח הזיכרון שנצרך. בממשק הזה אפשר גם להפעיל תמונת מצב של הזיכרון ומעקב אחרי הקצאות. הכלי הזה הוא ההמלצה המוגדרת כברירת מחדל. מידע נוסף זמין במאמר כלי לניתוחי זיכרון ב-Android Studio.
מוני הזיכרון של 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 בודק אם יש פעילות או קטע שעדיין אפשר לגשת אליהם אבל כבר הושמדו.
- תיעוד תמונת מצב הזיכרון של המערכת
- ניתוח של גרסת ה-dump של אשכול כדי למצוא דליפות זיכרון
- תיקון דליפות זיכרון.
פרטים נוספים מופיעים בסעיפים הבאים.
תיעוד תמונת מצב של הזיכרון
כדי לתעד dump של ערימה, אפשר להשתמש ב-Android Debug Bridge (adb) או ב-Android Studio Memory Profiler.
שימוש ב-adb כדי לתעד תמונת מצב של הזיכרון
כדי לצלם גרסת dump של אשכול באמצעות adb:
- מחברים את מכשיר Android למחשב.
- פותחים שורת פקודה ועוברים לספרייה שבה נמצאים הכלים של adb.
כדי לצלם גרסת dump של אשכול, מריצים את הפקודה הבאה:
adb shell am dumpheap my.app.name $PHONE_FILE_OUT
כדי לאחזר את האימג' של אשכול הזיכרון, מריצים את הפקודה הבאה:
adb pull $PHONE_FILE_OUT $LOCAL_FILE.
שימוש ב-Android Studio כדי לתעד dump של ערימה
כדי לתעד dump ערימה באמצעות Android Studio Memory Profiler, פועלים לפי השלבים שמפורטים בקטע תיעוד heapdump ב-Android.
ניתוח של תמונת המצב של הזיכרון כדי למצוא דליפות זיכרון
אחרי שתצלמו dump של ערימה, תוכלו להשתמש ב-Android Studio Memory Profiler כדי לנתח אותו. לשם כך, בצע את הצעדים הבאים:
פותחים את פרויקט Android ב-Android Studio.
בוחרים באפשרות Run (הפעלה) ואז באפשרות Debug (ניפוי באגים).
פותחים את הכרטיסייה Android Profiler.
בוחרים באפשרות זיכרון.
בוחרים באפשרות פתיחת תמונת מצב של הזיכרון ובוחרים את הקובץ של תמונת המצב של הזיכרון שיצרתם. בפרופילר הזיכרון מוצג תרשים של השימוש בזיכרון של האפליקציה.
משתמשים בתרשים כדי לנתח את תמונת המצב של הזיכרון:
לזהות אובייקטים שלא בשימוש יותר.
זיהוי אובייקטים שצורכים הרבה זיכרון.
לראות כמה זיכרון כל אובייקט משתמש בו.
אפשר להשתמש במידע הזה כדי לצמצם או למצוא את מקור דליפת הזיכרון ולתקן אותו.
שלב 5: תיקון דליפות זיכרון
אחרי שמזהים את המקור של דליפת הזיכרון, אפשר לתקן את הבעיה. תיקון דליפות זיכרון באפליקציות ל-Android עוזר לשפר את הביצועים והיציבות של האפליקציות. הפרטים משתנים בהתאם לתרחיש. עם זאת, ההצעות הבאות יכולות לעזור:
חשוב לוודא שהאפליקציה מקצה ומפנה זיכרון בהתאם להמלצות במאמר בנושא Android ניהול הזיכרון של האפליקציה.
מסירים מהאפליקציה קוד או משאבים שלא בשימוש. למידע נוסף על אפליקציות ל-Android, ראו שיטות מומלצות לאפליקציות ל-Android.
כלים אחרים לניפוי באגים
אם עדיין לא מצאתם את דליפת הזיכרון ותיקנתם אותה, תוכלו לנסות את הכלים הבאים:
ניפוי באגים בזיכרון בקוד נייטיב עם מעקב הקצאה
גם אם אתם לא משתמשים ישירות בקוד מקורי, כמה ספריות נפוצות ל-Android עושות זאת, כולל ערכות ה-SDK של Google. אם אתם חושבים שדליפה בזיכרון נובעת מקוד מקורי, יש כמה כלים שאפשר להשתמש בהם כדי לנפות באגים. מעקב אחר הקצאות באמצעות Android Studio או heapprofd (גם תואם ל-Perfetto) הוא דרך מצוינת לזהות את הסיבות האפשריות לדליפת זיכרון, ולרוב זו הדרך המהירה ביותר לנפות באגים.
מעקב אחר הקצאות הוא גם יתרון משמעותי, כי הוא מאפשר לכם לשתף את התוצאות בלי לכלול מידע רגיש שאפשר למצוא ב-heap.
זיהוי דליפות באמצעות LeakCanary
LeakCanary הוא כלי רב עוצמה לזיהוי דליפות זיכרון באפליקציות ל-Android. מידע נוסף על השימוש ב-LeakCanary באפליקציה שלכם זמין ב-LeakCanary.
איך מדווחים על בעיות ב-Google SDKs
אם ניסיתם את השיטות במסמך הזה ואתם חושדים שיש דליפת זיכרון בערכות ה-SDK שלנו, תוכלו לפנות לתמיכת הלקוחות ולצרף כמה שיותר פרטים:
השלבים לשחזור דליפת הזיכרון אם השלבים כוללים קוד מורכב, מומלץ להעתיק את הקוד שמשכפל את הבעיה לאפליקציה לדוגמה שלנו ולציין את השלבים הנוספים שצריך לבצע בממשק המשתמש כדי להפעיל את הדליפה.
קובצי dump של אשכול שנוצרו מהאפליקציה עם הבעיה שנוצרה מחדש. תיעוד של דמפים של אשכול (heap) בשני נקודות זמן שונות, שמראים שהשימוש בזיכרון גדל במידה משמעותית.
אם צפוי דליפת זיכרון מקומית, יש לשתף את הפלט של מעקב ההקצאות מ-heapprofd.
דוח על באג שנוצר אחרי שיצרתם מחדש את התנאי לדליפת מידע.
מעקב סטאק של קריסות שקשורות לזיכרון.
הערה חשובה: בדרך כלל, נתוני מעקב סטאק לא מספיקים בעצמם לניפוי באגים של בעיות זיכרון, לכן חשוב לספק גם אחד מהפורמטים האחרים של מידע.