אפשר ליצור גרסת dump של אשכול כדי לראות אילו אובייקטים באפליקציה צורכים זיכרון בזמן היצירה, ולזהות דליפות זיכרון או התנהגות של הקצאת זיכרון שמובילה לגמגום, לקיפאון ואפילו לקריסות של האפליקציה. כדאי במיוחד ליצור גרסת dump של אשכול אחרי סשן משתמש ממושך, כי יכול להיות שיופיעו בה אובייקטים שעדיין נמצאים בזיכרון אבל לא אמורים להיות שם.
בדף הזה מתוארים הכלים ש-Android Studio מספקת לאיסוף ולניתוח של דמפים של אשכול. לחלופין, אפשר לבדוק את הזיכרון של האפליקציה משורת הפקודה באמצעות dumpsys
וגם לראות אירועי איסוף אשפה (GC) ב-Logcat.
למה כדאי לבצע פרופיל של זיכרון האפליקציה
Android מספק סביבה של זיכרון מנוהל – כשמערכת Android קובעת שהאפליקציה כבר לא משתמשת באובייקטים מסוימים, מנקה האשפה משחרר את הזיכרון שלא מנוצל בחזרה לאוסף. אנחנו כל הזמן משפרים את האופן שבו Android מאתרת זיכרון שלא בשימוש, אבל בשלב מסוים בכל הגרסאות של Android, המערכת חייבת להשהות את הקוד לזמן קצר. ברוב המקרים, ההשהיות לא מורגשות. עם זאת, אם האפליקציה מקצה זיכרון מהר יותר מהמערכת יכולה לאסוף אותו, יכול להיות שהאפליקציה תתעכב בזמן שהאוסף משחרר מספיק זיכרון כדי לעמוד במכסות שהוקצו. העיכוב עלול לגרום לכך שהאפליקציה תדלג על פריימים, וכתוצאה מכך היא תאט באופן ניכר.
גם אם האפליקציה לא איטית, אם יש בה דליפת זיכרון, היא יכולה לשמור את הזיכרון הזה גם כשהיא פועלת ברקע. ההתנהגות הזו עלולה להאט את שאר הביצועים של הזיכרון במערכת על ידי אילוץ אירועי איסוף אשפה מיותרים. בסופו של דבר, המערכת נאלצת להרוג את תהליך האפליקציה כדי לפנות את הזיכרון. לאחר מכן, כשהמשתמש יחזור לאפליקציה, תהליך האפליקציה יצטרך להתחיל מחדש לגמרי.
במאמר ניהול הזיכרון של האפליקציה מפורט מידע על שיטות תכנות שיכולות לצמצם את השימוש בזיכרון של האפליקציה.
סקירה כללית על תמונת מצב של הזיכרון
כדי לצלם תמונת מצב של אשכול, בוחרים את המשימה Analyze Memory Usage (Heap Dump) (משתמשים ב-Profiler: run 'app' as debuggable (complete data)) כדי לצלם תמונת מצב של אשכול. במהלך ה-dump של ה-heap, יכול להיות שגודל הזיכרון של Java יגדל באופן זמני. זהו מצב תקין, כי תמונת המצב של אשכול הזיכרון מתבצעת באותו תהליך שבו פועלת האפליקציה, ונדרשת כמות מסוימת של זיכרון כדי לאסוף את הנתונים. אחרי שתצלמו את האשפה, תוצג ההודעה הבאה:
ברשימה של הכיתות מוצגים הפרטים הבאים:
- Allocations: מספר ההקצאות ב-heap.
Native Size: נפח הזיכרון המקורי הכולל שבו נעשה שימוש על ידי סוג האובייקט הזה (בבייטים). כאן יופיע זיכרון של אובייקטים מסוימים שהוקצו ב-Java, כי Android משתמשת בזיכרון מקומי לכמה כיתות של framework, כמו
Bitmap
.Shallow Size: סך כל הזיכרון ב-Java שבו נעשה שימוש על ידי סוג האובייקט הזה (בבייטים).
Retained Size: הנפח הכולל של הזיכרון שנשמר בגלל כל המופעים של הכיתה הזו (בבייטים).
אפשר להשתמש בתפריט ה-heap כדי לסנן לפי אשכולות מסוימים:
- App heap (ברירת מחדל): אשכול הזיכרון הראשי שאליו האפליקציה מקצה זיכרון.
- Image heap: קובץ האתחול של המערכת, שמכיל כיתות שנטענו מראש במהלך האתחול. ההקצאות האלה לא נעלמות או נעות.
- Zygote heap: אשכול ה-copy-on-write שממנו מתבצעת ההסתעפות של תהליך האפליקציה במערכת Android.
בתפריט הנפתח 'סידור' בוחרים איך לסדר את ההקצאות:
- סידור לפי כיתה (ברירת המחדל): קיבוץ כל ההקצאות לפי שם הכיתה.
- סידור לפי חבילה: קיבוץ כל ההקצאות לפי שם החבילה.
אפשר להשתמש בתפריט הנפתח של הכיתות כדי לסנן לקבוצות של כיתות:
- All classes (default): הצגת כל הכיתות, כולל אלה מספריות ומיחסי תלות.
- הצגת דליפות פעילות/קטע קוד: הצגת הכיתות שגורמות לדליפות זיכרון.
- הצגת כיתות הפרויקט: מוצגות רק כיתות שהוגדרו בפרויקט.
לוחצים על שם מחלקה כדי לפתוח את החלונית מכונה. כל מכונה ברשימה כוללת את הפרטים הבאים:
- עומק: מספר הקפיצות הקצר ביותר מכל שורש GC למכונה שנבחרה.
- Native Size: הגודל של המכונה הזו בזיכרון מקומי. העמודה הזו גלויה רק במכשירי Android מגרסה 7.0 ואילך.
- Shallow Size: הגודל של המכונה הזו בזיכרון Java.
- Retained Size: גודל הזיכרון שבו המופע הזה שולט (לפי עץ הדומיננטים).
לוחצים על מכונה כדי להציג את פרטי המכונה, כולל השדות וההפניות שלה. סוגי שדות וקובצי עזר נפוצים הם סוגי נתונים מובְנים , מערכי נתונים וסוגי נתונים פרימיטיביים ב-Java. לוחצים לחיצה ימנית על שדה או על הפניה כדי לעבור למופע או לשורה המשויכים בקוד המקור.
- שדות: הצגת כל השדות במופע הזה.
- References: כאן מוצגות כל ההפניות לאובייקט שמודגש בכרטיסייה Instance.
איתור דליפות זיכרון
כדי לסנן במהירות לכיתות שעשויות להיות קשורות לדליפות זיכרון, פותחים את התפריט הנפתח של הכיתות ובוחרים באפשרות Show activity/fragment leaks. ב-Android Studio מוצגות כיתות שלדעת המערכת מצביעות על דליפות זיכרון במכונות Activity
ו-Fragment
באפליקציה. סוגי הנתונים שמוצגים במסנן כוללים את הנתונים הבאים:
- מכונות
Activity
שהושמדו אבל עדיין יש להן הפניות. - מכונות
Fragment
ללאFragmentManager
תקף, אבל עדיין יש להן הפניות.
חשוב לדעת שהמסנן עלול להניב תוצאות חיוביות כוזבות במצבים הבאים:
- נוצר
Fragment
אבל עדיין לא נעשה בו שימוש. - ה-
Fragment
נשמר במטמון, אבל לא כחלק מ-FragmentTransaction
.
כדי לחפש דליפות זיכרון באופן ידני יותר, אפשר לעיין ברשימות הכיתות והמכונות כדי למצוא אובייקטים עם Retained Size גדול. מחפשים דליפות זיכרון שנגרמות בגלל אחת מהסיבות הבאות:
- הפניות לטווח ארוך ל-
Activity
,Context
,View
,Drawable
ואובייקטים אחרים שעשויים להכיל הפניה למאגרActivity
אוContext
. - כיתות פנימיות לא סטטיות, כמו
Runnable
, שיכולות להכיל מופע שלActivity
. - מטמון שמכיל אובייקטים למשך זמן ארוך מהנדרש.
אם מצאתם דליפות זיכרון פוטנציאליות, תוכלו להשתמש בכרטיסיות Fields ו-References בקטע Instance Details כדי לעבור לשורה הרלוונטית במכונה או בקוד המקור.
הפעלת דליפות זיכרון לצורך בדיקה
כדי לנתח את השימוש בזיכרון, צריך להפעיל לחץ על קוד האפליקציה ולנסות לאלץ דליפות זיכרון. אחת הדרכים לגרום לנזילות זיכרון באפליקציה היא לאפשר לה לפעול במשך זמן מה לפני שבודקים את האוסף. דליפות עשויות להצטבר בחלק העליון של ההקצאות בערימה. עם זאת, ככל שהדליפה קטנה יותר, כך צריך להריץ את האפליקציה למשך זמן ארוך יותר כדי לראות אותה.
אפשר גם לגרום לדליפה בזיכרון באחת מהדרכים הבאות:
- מסובבים את המכשיר מפורטרט לרוחב וחזרה מספר פעמים במצבים שונים של פעילות. כיוון המכשיר יכול לגרום לאפליקציה לדלוף אובייקט
Activity
,Context
אוView
, כי המערכת יוצרת מחדש אתActivity
. אם האפליקציה שומרת הפניה לאחד מהאובייקטים האלה במקום אחר, המערכת לא יכולה לאסוף את האובייקט הזה. - לעבור בין האפליקציה שלכם לאפליקציה אחרת במצבי פעילות שונים. לדוגמה, מנווטים למסך הבית ואז חוזרים לאפליקציה.
ייצוא וייבוא של הקלטה של גרסת dump של אשכול
אפשר לייצא ולייבא קובץ דמפ של אשכול מהכרטיסייה Past Recordings (הקלטות קודמות) בפרופילר. Android Studio שומר את ההקלטה כקובץ .hprof
.
לחלופין, כדי להשתמש בניתוח קובץ .hprof
אחר, כמו jhat, צריך להמיר את קובץ ה-.hprof
מפורמט Android לפורמט קובץ Java SE .hprof
. כדי להמיר את פורמט הקובץ, משתמשים בכלי hprof-conv
שמסופק בספרייה {android_sdk}/platform-tools/
. מריצים את הפקודה hprof-conv
עם שני ארגומנטים: שם הקובץ המקורי של .hprof
והמיקום שבו רוצים לכתוב את קובץ ה-.hprof
המומר, כולל שם הקובץ החדש של .hprof
. לדוגמה:
hprof-conv heap-original.hprof heap-converted.hprof