במסמך הזה אנחנו יוצאים מנקודת הנחה שפעלתם לפי ההנחיות לשיטות מומלצות לגבי אפליקציות ל-Android בנושא ניהול זיכרון, כמו ניהול הזיכרון של האפליקציה.
מבוא
דליפת זיכרון היא סוג של דליפת משאבים שמתרחשת כשתוכנת מחשב לא משחררת זיכרון שהוקצה לה, שכבר לא נחוץ. דליפה עלולה לגרום לאפליקציה לבקש מהמערכת יותר זיכרון ממה שיש לה, וכך לגרום לקריסת האפליקציה. יש כמה שיטות לא תקינות שעלולות לגרום לדליפות זיכרון באפליקציות ל-Android, כמו אי-סילוק נכון של משאבים או אי-ביטול רישום של מאזינים כשאין בהם יותר צורך.
במסמך הזה מפורטות כמה שיטות מומלצות שיעזרו לכם למנוע דליפות זיכרון בקוד, לזהות אותן ולפתור אותן. אם ניסיתם את השיטות שמתוארות במסמך הזה ואתם חושדים שיש דליפת זיכרון בערכות ה-SDK שלנו, כדאי לעיין במאמר איך מדווחים על בעיות בערכות SDK של Google.
לפני שפונים לתמיכה
לפני שמדווחים על דליפת זיכרון לצוות התמיכה של Google, כדאי לפעול לפי השיטות המומלצות ולבצע את שלבי הניפוי שמופיעים במסמך הזה כדי לוודא שהשגיאה לא נמצאת בקוד. יכול להיות שהשלבים האלה יפתרו את הבעיה, ואם לא, הם ייצרו את המידע שצוות התמיכה של Google צריך כדי לעזור לכם.
מניעת דליפות זיכרון
כדי להימנע מכמה מהגורמים הנפוצים ביותר לזליגת זיכרון בקוד שמשתמש בערכות SDK של Google, מומלץ לפעול לפי השיטות המומלצות הבאות.
שיטות מומלצות לאפליקציות ל-Android
בודקים שביצעתם את כל הפעולות הבאות באפליקציית Android:
- הפצת משאבים שלא נמצאים בשימוש.
- ביטול הרישום של מאזינים כשאין בהם יותר צורך.
- ביטול משימות כשאין בהן צורך.
- העברת שיטות של מחזור חיים כדי לשחרר משאבים
- שימוש בגרסאות האחרונות של ערכות ה-SDK
בסעיפים הבאים מפורטים פרטים ספציפיים לגבי כל אחת מהשיטות האלה.
הפצת משאבים שלא נמצאים בשימוש
כשמשתמשים במשאב באפליקציית Android, חשוב לשחרר את המשאב כשכבר לא צריך אותו. אם לא תעשו זאת, המשאב ימשיך לתפוס זיכרון גם אחרי שהאפליקציה תסיים את השימוש בו. מידע נוסף זמין במאמר בנושא מחזור החיים של פעילות במסמכי התיעוד של Android.
שחרור הפניות לא עדכניות ל-GoogleMap ב-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
כדי להאזין לאירועי הגעה, היא צריכה גם לקרוא ל-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 של Navigation או Maps, צריך לוודא שמשחררים את המשאבים על ידי העברת שיטות מחזור החיים (מוצגות בהדגשה) אל 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 של ה-heap ומעקב אחר הקצאת הזיכרון, ואז ניתוח שלהם.
- פתרון בעיות של דליפת זיכרון.
בקטעים הבאים מוסבר בהרחבה על השלבים האלה.
שלב 1: משחזרים את הבעיה
אם לא הצלחתם לשחזר את הבעיה, כדאי קודם לשקול את התרחישים שיכולים לגרום לדליפת הזיכרון. אם יודעים שהבעיה נוצרה מחדש, אפשר לעבור ישר לבדיקת dump של ה-heap. עם זאת, אם מקבלים dump של ה-heap בהפעלת האפליקציה או בנקודה אקראית אחרת בזמן, יכול להיות שלא הפעלתם את התנאים להפעלת דליפה. כדאי לנסות לשחזר את הבעיה באמצעות תרחישים שונים:
אילו תכונות מופעלות?
מהו הרצף הספציפי של פעולות המשתמש שגורם לדליפה?
- האם ניסית להפעיל את הרצף הזה כמה פעמים?
באילו מצבים במחזור החיים האפליקציה עברה?
- ניסיתם כמה איטרציות במצבי מחזור חיים שונים?
מוודאים שאפשר לשחזר את הבעיה בגרסה העדכנית של ערכות ה-SDK. יכול להיות שהבעיה מגרסה קודמת כבר תוקנה.
שלב 2: בדיקה אם השימוש בזיכרון של האפליקציה תואם לציפיות
כל תכונה דורשת זיכרון נוסף. כשמבצעים ניפוי באגים בתרחישים שונים, צריך לבדוק אם מדובר בשימוש צפוי או בדליפת זיכרון. לדוגמה, כשמדובר בתכונות שונות או במשימות משתמש שונות, אפשר להשתמש באפשרויות הבאות:
סביר להניח שיש דליפה: הפעלת התרחיש בכמה איטרציות גורמת לגידול בשימוש בזיכרון לאורך זמן.
השימוש הצפוי בזיכרון: הזיכרון משוחרר אחרי שהתרחיש מופסק.
שימוש בזיכרון שאולי צפוי: השימוש בזיכרון עולה למשך תקופה מסוימת ואז יורד. יכול להיות שהסיבה לכך היא מטמון מוגבל או שימוש צפוי אחר בזיכרון.
אם סביר להניח שהתנהגות האפליקציה היא שימוש צפוי בזיכרון, אפשר לפתור את הבעיה באמצעות ניהול הזיכרון של האפליקציה. תוכלו להיעזר במאמר בנושא ניהול הזיכרון של האפליקציה.
שלב 3: ניפוי באגים ברמה גבוהה
כשמנפים באגים של דליפת זיכרון, מתחילים ברמה גבוהה ואז מתמקדים אחרי שמצמצמים את האפשרויות. כדי לבדוק אם יש דליפה לאורך זמן, אפשר להשתמש באחד מכלי הניפוי הבאים ברמה גבוהה:
Android Studio Memory Profiler (מומלץ)
Android Studio Memory Profiler
הכלי הזה מספק היסטוגרמה חזותית של הזיכרון שנצרך. אפשר להפעיל מאותו ממשק גם תמונות מצב של הזיכרון ומעקב אחר הקצאת הזיכרון. הכלי הזה הוא ההמלצה שמוגדרת כברירת מחדל. מידע נוסף זמין במאמר בנושא כלי פרופיל הזיכרון ב-Android Studio.
Perfetto Memory Counters
Perfetto מאפשר לכם לשלוט באופן מדויק במעקב אחרי כמה מדדים, ומציג את כולם בהיסטוגרמה אחת. מידע נוסף זמין במאמר בנושא Perfetto Memory Counters.
כלי שורת פקודה של ממשק הגישור של Android (ADB)
רוב הנתונים שאפשר לעקוב אחריהם באמצעות Perfetto זמינים גם ככלי שורת פקודה שאפשר לשלוח לו שאילתות ישירות.adb
הנה כמה דוגמאות חשובות:
Meminfo מאפשר לכם לראות מידע מפורט על הזיכרון בנקודת זמן מסוימת.
Procstats מספק כמה נתונים סטטיסטיים חשובים שמצטברים לאורך זמן.
נתון סטטיסטי חשוב שכדאי לבדוק כאן הוא טביעת הרגל המקסימלית של הזיכרון הפיזי (maxRSS) שהאפליקציה דורשת לאורך זמן. יכול להיות שהערך של MaxPSS לא יהיה מדויק. כדי לשפר את רמת הדיוק, אפשר להשתמש בדגל adb shell dumpsys procstats --help –start-testing
.
מעקב אחר הקצאות
מעקב ההקצאה מזהה את דוח הקריסות שבו הוקצה זיכרון, ואם הזיכרון לא שוחרר. השלב הזה שימושי במיוחד כשמנסים לאתר דליפות בקוד מקורי. הכלי הזה מזהה את עקבות המחסנית, ולכן הוא יכול לעזור לכם לנפות באגים במהירות כדי לגלות את שורש הבעיה או להבין איך לשחזר אותה. הוראות לשימוש במעקב אחר הקצאות מפורטות במאמר ניפוי באגים בזיכרון בקוד נייטיב באמצעות מעקב אחר הקצאות.
שלב 4: בדיקת השימוש בזיכרון של האפליקציה באמצעות dump של ה-heap
אחת הדרכים לזהות דליפת זיכרון היא לקבל heap dump של האפליקציה ואז לבדוק אם יש דליפות. heap dump הוא תמונת מצב של כל האובייקטים בזיכרון של אפליקציה. אפשר להשתמש בו כדי לאבחן דליפות זיכרון ובעיות אחרות שקשורות לזיכרון.
Android Studio יכול לזהות דליפות זיכרון שלא ניתן לתקן באמצעות GC. כשמבצעים צילום מצב של ה-heap, Android Studio בודק אם יש פעילות או קטע שעדיין אפשר להגיע אליהם אבל הם כבר נהרסו.
בסעיפים הבאים יש מידע נוסף בנושא.
תיעוד תמונת מצב של הזיכרון
כדי לתעד dump ערימה, אפשר להשתמש ב-Android Debug Bridge (adb) או בכלי Memory Profiler של Android Studio.
שימוש ב-adb כדי לצלם תמונת מצב של הזיכרון
כדי ליצור dump של ה-heap באמצעות adb, פועלים לפי השלבים הבאים:
- מחברים את מכשיר Android למחשב.
- פותחים שורת פקודה ועוברים לספרייה שבה נמצאים כלי ה-adb.
כדי ליצור dump של ה-heap, מריצים את הפקודה הבאה :
adb shell am dumpheap my.app.name $PHONE_FILE_OUT
כדי לאחזר את ה-heap dump, מריצים את הפקודה הבאה:
adb pull $PHONE_FILE_OUT $LOCAL_FILE.
שימוש ב-Android Studio כדי לתעד dump ערימה
כדי לתעד dump ערימה באמצעות הכלי Memory Profiler ב-Android Studio, צריך לפעול לפי השלבים שמפורטים בקטע Capture a heapdump במאמר בנושא Android.
ניתוח של תמונת המצב של הזיכרון כדי למצוא דליפות זיכרון
אחרי שתתעדו dump ערימה, תוכלו להשתמש בכלי Memory Profiler של Android Studio כדי לנתח אותו. לשם כך, בצע את הצעדים הבאים:
פותחים את פרויקט Android ב-Android Studio.
בוחרים באפשרות Run (הרצה) ואז בוחרים בהגדרת Debug (ניפוי באגים).
פותחים את הכרטיסייה Android Profiler.
בוחרים באפשרות זיכרון.
בוחרים באפשרות Open heap dump (פתיחת dump של ה-heap) ובוחרים את קובץ ה-dump של ה-heap שיצרתם. בכלי ליצירת פרופיל זיכרון מוצג תרשים של השימוש בזיכרון של האפליקציה.
משתמשים בתרשים כדי לנתח את ה-heap dump:
לזהות אובייקטים שכבר לא נמצאים בשימוש.
זיהוי אובייקטים שצורכים הרבה זיכרון.
כמה זיכרון כל אובייקט צורך.
המידע הזה יכול לעזור לכם לצמצם את החיפוש או למצוא את המקור של דליפת הזיכרון ולתקן אותה.
שלב 5: פותרים בעיות של דליפות זיכרון
אחרי שמזהים את המקור של דליפת הזיכרון, אפשר לתקן אותה. תיקון של דליפות זיכרון באפליקציות ל-Android עוזר לשפר את הביצועים ואת היציבות של האפליקציות. הפרטים משתנים בהתאם לתרחיש. אבל אולי ההצעות הבאות יעזרו לך:
מוודאים שהאפליקציה מקצה זיכרון ומבטל את ההקצאה שלו בהתאם להמלצות בנושא ניהול הזיכרון של האפליקציה ב-Android.
מסירים מהאפליקציה קוד או משאבים שלא נמצאים בשימוש. לפרטים על אפליקציות ל-Android, אפשר לעיין במאמר שיטות מומלצות לאפליקציות ל-Android.
כלים אחרים לניפוי באגים
אם אחרי השלבים האלה עדיין לא מצאתם ותיקנתם את דליפת הזיכרון, נסו את הכלים הבאים:
ניפוי באגים בזיכרון בקוד נייטיב באמצעות מעקב אחר הקצאות
גם אם אתם לא משתמשים ישירות בקוד מקורי, כמה ספריות נפוצות של Android כן משתמשות בקוד כזה, כולל ערכות SDK של Google. אם אתם חושבים שיש דליפת זיכרון בקוד מקורי, תוכלו להשתמש בכמה כלים כדי לנפות באגים. מעקב אחר הקצאת זיכרון באמצעות Android Studio או heapprofd (שגם תואם ל-Perfetto) הוא דרך מצוינת לזהות את הסיבות האפשריות לדליפת זיכרון, ולרוב זו הדרך המהירה ביותר לניפוי באגים.
יתרון נוסף של מעקב אחר הקצאות הוא האפשרות לשתף את התוצאות בלי לכלול מידע רגיש שאפשר למצוא בזיכרון.
זיהוי דליפות באמצעות LeakCanary
LeakCanary הוא כלי יעיל לזיהוי דליפות זיכרון באפליקציות ל-Android. מידע נוסף על השימוש ב-LeakCanary באפליקציה זמין בכתובת LeakCanary.
איך מדווחים על בעיות בערכות SDK של Google
אם ניסיתם את השיטות שמתוארות במסמך הזה ואתם חושדים בדליפת זיכרון בערכות ה-SDK שלנו, פנו לתמיכת הלקוחות וציינו כמה שיותר מהפרטים הבאים:
השלבים לשחזור דליפת הזיכרון. אם השלבים דורשים קידוד מורכב, כדאי להעתיק את הקוד שמשחזר את הבעיה לאפליקציה לדוגמה שלנו ולספק שלבים נוספים שצריך לבצע בממשק המשתמש כדי להפעיל את הדליפה.
קובצי Heap Dump שצולמו מהאפליקציה עם שחזור הבעיה. מצלמים שני dump של ה-heap בנקודות זמן שונות, שבהן רואים שהשימוש בזיכרון גדל באופן משמעותי.
אם צפוי דליפת זיכרון מקומית, צריך לשתף את פלט המעקב אחר ההקצאה מ-heapprofd.
דוח על באג שנוצר אחרי ששחזרתם את התנאים שגורמים לדליפה.
עקבות מחסנית של קריסות שקשורות לזיכרון.
הערה חשובה: בדרך כלל, עקבות מחסנית לא מספיקים כדי לנפות באגים בבעיה שקשורה לזיכרון, לכן חשוב לספק גם אחד מסוגי המידע האחרים.