במסמך הזה אנחנו יוצאים מנקודת הנחה שפעלתם לפי ההנחיות לשיטות מומלצות לגבי אפליקציות ל-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 (adb).
- בודקים את השימוש בזיכרון של האפליקציה. קבלת dump של ה-heap ומעקב אחר הקצאת הזיכרון, ואז ניתוח שלהם.
- פתרון בעיות של דליפת זיכרון.
בקטעים הבאים מוסבר בהרחבה על השלבים האלה.
שלב 1: משחזרים את הבעיה
אם לא הצלחתם לשחזר את הבעיה, כדאי קודם לשקול את התרחישים שיכולים לגרום לדליפת הזיכרון. אם יודעים שהבעיה נוצרה מחדש, אפשר לעבור ישר לבדיקת קובץ ה-heap dump. עם זאת, אם מקבלים 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 של האפליקציה ואז לבדוק אם יש דליפות. תמונת מצב של הערימה היא תמונת מצב של כל האובייקטים בזיכרון של האפליקציה. אפשר להשתמש בו כדי לאבחן דליפות זיכרון ובעיות אחרות שקשורות לזיכרון.
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.
כלים אחרים לניפוי באגים
אם אחרי השלבים האלה עדיין לא מצאתם ותיקנתם את דליפת הזיכרון, נסו את הכלים הבאים:
ניפוי באגים בזיכרון בקוד נייטיב באמצעות מעקב אחר הקצאות
גם אם אתם לא משתמשים ישירות בקוד Native, יש כמה ספריות נפוצות של Android שכן משתמשות בו, כולל ערכות SDK של Google. אם אתם חושבים שיש דליפת זיכרון בקוד מקורי, תוכלו להשתמש בכמה כלים כדי לנפות באגים. מעקב אחר הקצאת זיכרון באמצעות Android Studio או heapprofd (שגם תואם ל-Perfetto) היא דרך מצוינת לזהות גורמים פוטנציאליים לדליפת זיכרון, ולרוב זו הדרך המהירה ביותר לניפוי באגים.
יתרון נוסף של מעקב אחר הקצאות הוא האפשרות לשתף את התוצאות בלי לכלול מידע רגיש שאפשר למצוא בזיכרון.
זיהוי דליפות באמצעות LeakCanary
LeakCanary הוא כלי רב עוצמה לזיהוי דליפות זיכרון באפליקציות ל-Android. מידע נוסף על השימוש ב-LeakCanary באפליקציה זמין בכתובת LeakCanary.
איך מדווחים על בעיות בערכות SDK של Google
אם ניסיתם את השיטות שמתוארות במסמך הזה ואתם חושדים בדליפת זיכרון בערכות ה-SDK שלנו, פנו לתמיכת הלקוחות וציינו כמה שיותר מהפרטים הבאים:
השלבים לשחזור דליפת הזיכרון. אם השלבים דורשים קידוד מורכב, כדאי להעתיק את הקוד שמשחזר את הבעיה לאפליקציה לדוגמה שלנו ולספק שלבים נוספים שצריך לבצע בממשק המשתמש כדי להפעיל את הדליפה.
קובצי Heap Dump שצולמו מהאפליקציה אחרי שיצרת מחדש את הבעיה. מצלמים שני dump של ה-heap בנקודות זמן שונות, שבהן רואים שהשימוש בזיכרון גדל באופן משמעותי.
אם צפוי דליפת זיכרון מקומית, צריך לשתף את פלט המעקב אחר ההקצאה מ-heapprofd.
דוח על באג שנוצר אחרי שיצרתם מחדש את התנאי שגורם לדליפה.
עקבות מחסנית של קריסות שקשורות לזיכרון.
הערה חשובה: בדרך כלל, עקבות מחסנית לא מספיקים כדי לנפות באגים בבעיה שקשורה לזיכרון, לכן חשוב לספק גם אחד מסוגי המידע האחרים.