אפשר לצלם תמונת מצב של הזיכרון כדי לראות אילו אובייקטים באפליקציה תופסים זיכרון בזמן הצילום, ולזהות דליפות זיכרון או התנהגות של הקצאת זיכרון שמובילה לגמגום, לקפיאות ואפילו לקריסות של האפליקציה. מומלץ במיוחד ליצור תמונות מצב של הערימה אחרי הפעלת המשתמש למשך זמן ממושך, כי הן יכולות להראות אובייקטים שעדיין נמצאים בזיכרון אבל לא אמורים להיות שם יותר.
בדף הזה מתואר כלי ש-Android Studio מספק לאיסוף ולניתוח של נתוני heap dump. לחלופין, אפשר לבדוק את הזיכרון של האפליקציה משורת הפקודה באמצעות dumpsys וגם לראות אירועים של מנגנון איסוף זבל (GC) ב-Logcat.
למה כדאי ליצור פרופיל של זיכרון האפליקציה
Android מספק סביבת זיכרון מנוהלת – כש-Android קובע שהאפליקציה כבר לא משתמשת בחלק מהאובייקטים, איסוף האשפה משחרר את הזיכרון שלא בשימוש בחזרה ל-heap. השיטה שבה מערכת Android מוצאת זיכרון לא בשימוש משתפרת כל הזמן, אבל בשלב מסוים בכל הגרסאות של Android, המערכת חייבת להשהות את הקוד שלכם לזמן קצר. ברוב המקרים, ההשהיות לא מורגשות. עם זאת, אם האפליקציה מקצה זיכרון מהר יותר מהקצב שבו המערכת יכולה לאסוף אותו, יכול להיות שהאפליקציה תתעכב בזמן שהמערכת מפנה מספיק זיכרון כדי לענות על ההקצאות. העיכוב עלול לגרום לאפליקציה לדלג על פריימים ולגרום לאיטיות נראית לעין.
גם אם האפליקציה לא פועלת לאט, אם יש בה דליפת זיכרון, היא יכולה לשמור את הזיכרון הזה גם כשהיא פועלת ברקע. ההתנהגות הזו עלולה להאט את הביצועים של שאר הזיכרון במערכת, כי היא גורמת לאירועים מיותרים של איסוף זבל. בסופו של דבר, המערכת נאלצת להפסיק את תהליך האפליקציה כדי לפנות זיכרון. לאחר מכן, כשהמשתמש חוזר לאפליקציה, תהליך האפליקציה חייב להתחיל מחדש לגמרי.
מידע על שיטות תכנות שיכולות לצמצם את השימוש בזיכרון של האפליקציה זמין במאמר ניהול הזיכרון של האפליקציה.
סקירה כללית של תמונת מצב של הזיכרון
כדי לצלם תמונת מצב של הזיכרון, בוחרים במשימה Analyze Memory Usage (Heap Dump) (משתמשים ב-Profiler: run 'app' as debuggable (complete data)) כדי לצלם תמונת מצב של הזיכרון. במהלך יצירת ה-dump של ה-heap, כמות הזיכרון של Java עשויה לגדול באופן זמני. זה תקין כי תמונת המצב של הזיכרון נוצרת באותו תהליך של האפליקציה, ונדרש זיכרון כדי לאסוף את הנתונים. אחרי שמבצעים צילום של ה-heap dump, מוצגות האפשרויות הבאות:
ברשימת הכיתות מוצגים הפרטים הבאים:
- הקצאות: מספר ההקצאות בערימה.
גודל מקורי: הכמות הכוללת של הזיכרון המקורי שנעשה בו שימוש על ידי סוג האובייקט הזה (בבייט). כאן מוצג הזיכרון של חלק מהאובייקטים שהוקצו ב-Java, כי מערכת Android משתמשת בזיכרון מקומי עבור חלק ממחלקות ה-framework, כמו
Bitmap.Shallow Size: הכמות הכוללת של זיכרון Java שנעשה בה שימוש על ידי סוג האובייקט הזה (בבייטים).
הנפח שמתפנה: הגודל הכולל של הזיכרון שנשמר בגלל כל המופעים של המחלקה הזו (בבייטים).
משתמשים בתפריט הערימה כדי לסנן ערימות מסוימות:
- זיכרון Heap של האפליקציה (ברירת מחדל): הזיכרון הראשי שבו האפליקציה מקצה זיכרון.
- Image heap: קובץ אימג' לאתחול המערכת, שמכיל מחלקות שנטענות מראש במהלך זמן האתחול. הקצאות שמוגדרות כאן לא משתנות ולא נעלמות.
- Zygote heap: ה-heap של העתקה בעת כתיבה שממנו מתבצע פיצול של תהליך האפליקציה במערכת Android.
משתמשים בתפריט הנפתח 'סידור' כדי לבחור איך לסדר את ההקצאות:
- סידור לפי כיתה (ברירת מחדל): כל ההקצאות מקובצות לפי שם הכיתה.
- סידור לפי חבילה: כל ההקצאות מקובצות לפי שם החבילה.
משתמשים בתפריט הנפתח של הכיתות כדי לסנן קבוצות של כיתות:
- כל השיעורים (ברירת מחדל): מוצגים כל השיעורים, כולל אלה מהספריות ומהתלויות.
- הצגת דליפות של פעילות או של מקטעי קוד: הצגת מחלקות שגורמות לדליפות זיכרון.
- הצגת כיתות בפרויקט: הצגת כיתות שהוגדרו רק בפרויקט.
לוחצים על שם של מחלקה כדי לפתוח את החלונית Instance (מופע). כל מופע שמופיע ברשימה כולל את הפרטים הבאים:
- עומק: מספר הדילוגים הקצר ביותר מכל שורש GC למופע שנבחר.
- גודל מקורי: הגודל של המופע הזה בזיכרון המקורי. העמודה הזו גלויה רק ב-Android מגרסה 7.0 ואילך.
- גודל שטחי: הגודל של המופע הזה בזיכרון של Java.
- הגודל נשמר: גודל הזיכרון שמופע ה-DOM הזה תופס (כפי שמופיע בעץ ה-dominator).
לוחצים על מופע כדי להציג את פרטי המופע, כולל השדות וההפניות שלו. סוגים נפוצים של שדות והפניות הם סוגים מובנים
,
מערכים
,
וסוגי נתונים פרימיטיביים
ב-Java. לוחצים לחיצה ימנית על שדה או על הפניה כדי לעבור למופע או לשורה המשויכים בקוד המקור.
- שדות: מוצגים כל השדות במופע הזה.
- הפניות: מציג את כל ההפניות לאובייקט שמודגש בכרטיסייה מופע.
זיהוי מפות סיביות כפולות
החל מ-Android Studio Narwhal 4, אפשר גם לזהות מפות סיביות מיותרות בתצוגת Heap Dump.
כך מאתרים אותם:
- פתיחת הכרטיסייה Profiler ב-Android Studio
- לוחצים על Heap Dump (או על Analyze Memory Usage) ואז על record כדי לצלם תמונה של מצב הזיכרון הנוכחי של האפליקציה.
- סורקים את תוצאות הניתוח כדי למצוא את משולש האזהרה הצהוב ⚠️, שמשמש את Android Studio לסימון מפות סיביות כפולות שנשמרות כמה פעמים.
- אפשרות נוספת היא לעבור לכותרת של כלי הפרופיל, לבחור באפשרות Filter by: (סינון לפי:) ואז לבחור בהגדרה Duplicate Bitmaps (מפות סיביות כפולות).
- לוחצים על רשומה מסומנת כדי לפתוח את החלונית תצוגה מקדימה של מפת סיביות, שבה אפשר לראות בדיוק איזו תמונה היא העבריינית החוזרת.
- אפשר להשתמש באישור החזותי הזה כדי לאתר את הלוגיקה המיותרת של הטעינה בקוד ולהטמיע אסטרטגיית שמירה במטמון טובה יותר.
איך מוצאים דליפות זיכרון
כדי לסנן במהירות את הכיתות שאולי משויכות לדליפות זיכרון, פותחים את התפריט הנפתח של הכיתה ובוחרים באפשרות הצגת דליפות של פעילות או של מקטעים. ב-Android Studio מוצגות מחלקות שלדעת המערכת מצביעות על דליפות זיכרון עבור מופעים של Activity ושל Fragment באפליקציה.
כדי לחפש דליפות זיכרון באופן ידני יותר, אפשר לעיין ברשימות של המחלקות והמופעים כדי למצוא אובייקטים עם גודל שמור גדול. מחפשים דליפות זיכרון שנגרמות בגלל אחת מהסיבות הבאות:
- הפניות לטווח ארוך אל
Activityאו אלContextשעלולות לגרום לדליפה של גרף הקומפוזיציה של Compose שמתארח (למשלComposeViewוהרכיבים המשניים שלו). - הדלפה של אובייקטים של מצב Jetpack Compose (
MutableState), מאחסני מצב או ביטויי למדה שתופסיםContext. - שוכחים לנקות את המאזינים או את הצופים בבלוק
onDisposeשלDisposableEffect. - מחלקות פנימיות לא סטטיות, כמו
Runnable, שיכולות להכיל מופע שלActivity. - מטמון שמכיל אובייקטים למשך זמן ארוך מהנדרש.
כשמוצאים דליפות זיכרון פוטנציאליות, אפשר להשתמש בכרטיסיות Fields (שדות) ו-References (הפניות) ב-Instance Details (פרטי מופע) כדי לעבור למופע או לשורת קוד המקור הרלוונטיים.
הפעלת דליפות זיכרון לצורך בדיקה
כדי לנתח את השימוש בזיכרון, צריך להעמיס על קוד האפליקציה ולנסות לכפות דליפות זיכרון. דרך אחת לגרום לדליפות זיכרון באפליקציה היא להפעיל אותה למשך זמן מסוים לפני שבודקים את ה-heap. יכול להיות שהדליפות יגיעו לחלק העליון של ההקצאות בערימה. עם זאת, ככל שהדליפה קטנה יותר, כך צריך להריץ את האפליקציה יותר זמן כדי לראות אותה.
אפשר גם להפעיל דליפת זיכרון באחת מהדרכים הבאות:
- מסובבים את המכשיר ממצב לאורך למצב לרוחב ובחזרה כמה פעמים
בזמן שהוא במצבי פעילות שונים. סיבוב המכשיר יכול לגרום לאפליקציה לדלוף של
Activity(וכתוצאה מכך של עץ ממשק המשתמש של Compose שמתארח בו ועצי המצב המשויכים), אם האפליקציה מחזיקה בהפניה אלActivityאו אלContextבתוך פעולות אסינכרוניות או מחזיקי מצב. - לעבור בין האפליקציה שלכם לבין אפליקציה אחרת במצבי פעילות שונים. לדוגמה, עוברים למסך הבית ואז חוזרים לאפליקציה.
ייצוא וייבוא של הקלטת תמונת מצב של הזיכרון
אפשר לייצא ולייבא קובץ של תמונת מצב של הזיכרון מהכרטיסייה הקלטות קודמות בכלי לניתוח ביצועים (profiler). Android Studio שומר את ההקלטה כקובץ .hprof.
לחלופין, כדי להשתמש בכלי אחר לניתוח קבצים מסוג .hprof כמו jhat, צריך להמיר את קובץ .hprof מפורמט Android לפורמט קובץ .hprof Java SE. כדי להמיר את פורמט הקובץ, משתמשים בכלי hprof-conv שמופיע בספרייה {android_sdk}/platform-tools/. מריצים את הפקודה hprof-conv עם שני ארגומנטים: שם הקובץ המקורי .hprof והמיקום שבו רוצים לכתוב את הקובץ .hprof שהומר, כולל שם הקובץ החדש .hprof. לדוגמה:
hprof-conv heap-original.hprof heap-converted.hprof