תיעוד תמונת מצב של ערימה

כדי לראות אילו אובייקטים באפליקציה משתמשים בזיכרון בזמן הצילום, אפשר לצלם תמונת מצב של הזיכרון ולזהות דליפות זיכרון או התנהגות של הקצאת זיכרון שגורמת לקרטועים, לקפיאות ואפילו לקריסות של האפליקציה. כדאי במיוחד ליצור תמונות מצב של הזיכרון אחרי סשן משתמש ארוך, כי הן יכולות להראות אובייקטים שעדיין נמצאים בזיכרון אבל לא אמורים להיות שם יותר.

בדף הזה מתוארים הכלים ש-Android Studio מספקת כדי לאסוף ולנתח תמונות מצב של הזיכרון. לחלופין, אתם יכולים לבדוק את זיכרון האפליקציה משורת הפקודה באמצעות dumpsys וגם לראות אירועים של איסוף אשפה (GC) ב-Logcat.

למה כדאי ליצור פרופיל של זיכרון האפליקציה

מערכת Android מספקת סביבת זיכרון מנוהלת – כשהמערכת מזהה שהאפליקציה שלכם כבר לא משתמשת באובייקטים מסוימים, מנגנון איסוף האשפה משחרר את הזיכרון שלא בשימוש בחזרה לערימה. אנחנו משפרים כל הזמן את הדרך שבה מערכת Android מוצאת זיכרון לא מנוצל, אבל בשלב מסוים בכל הגרסאות של Android, המערכת צריכה להשהות את הקוד לזמן קצר. ברוב המקרים, ההפסקות לא מורגשות. עם זאת, אם האפליקציה מקצה זיכרון מהר יותר מהקצב שבו המערכת יכולה לאסוף אותו, יכול להיות שהאפליקציה תתעכב בזמן שהמערכת מפנה מספיק זיכרון כדי לעמוד בהקצאות. העיכוב עלול לגרום לאפליקציה לדלג על פריימים ולגרום לאיטיות ניכרת.

גם אם האפליקציה לא פועלת באיטיות, אם יש בה דליפת זיכרון, היא יכולה לשמור את הזיכרון הזה גם כשהיא פועלת ברקע. ההתנהגות הזו יכולה להאט את ביצועי הזיכרון של שאר המערכת, כי היא מאלצת אירועים מיותרים של איסוף אשפה. בסופו של דבר, המערכת נאלצת להפסיק את תהליך האפליקציה כדי לפנות את הזיכרון. כשהמשתמש חוזר לאפליקציה, תהליך האפליקציה צריך להתחיל מחדש לגמרי.

כדי לקבל מידע על שיטות תכנות שיכולות להפחית את השימוש בזיכרון של האפליקציה, קראו את המאמר ניהול הזיכרון של האפליקציה.

סקירה כללית של תמונת מצב של הזיכרון

כדי ליצור תמונת מצב של הזיכרון, בוחרים את המשימה Analyze Memory Usage (Heap Dump)‎ (משתמשים באפשרות Profiler: run 'app' as debuggable (complete data)‎). בזמן ה-dump של ה-heap, כמות הזיכרון של Java עשויה לגדול באופן זמני. זה מצב תקין, כי תמונת המצב של הזיכרון נוצרת באותו תהליך כמו האפליקציה, וצריך קצת זיכרון כדי לאסוף את הנתונים. אחרי שיוצרים את תמונת המצב של הזיכרון, מופיע המסך הבא:

תצוגת תמונת מצב של הזיכרון בכלי לניתוח ביצועים (profiler) של Android Studio.

ברשימת הכיתות מוצגים הפרטים הבאים:

  • הקצאות: מספר ההקצאות בערימה.
  • גודל מקורי: הכמות הכוללת של הזיכרון המקורי שסוג האובייקט הזה משתמש בו (בבייטים). כאן תראו זיכרון של אובייקטים מסוימים שהוקצו ב-Java, כי Android משתמשת בזיכרון מקורי בשביל מחלקות מסוימות של framework, כמו Bitmap.

  • ‫Shallow Size: כמות הזיכרון הכוללת של Java שסוג האובייקט הזה משתמש בה (בבייטים).

  • הנפח שמתפנה (retained size): הנפח הכולל של הזיכרון שנשמר בגלל כל המופעים של המחלקה הזו (בבייטים).

אפשר להשתמש בתפריט הערימה כדי לסנן ערימות מסוימות:

  • ערימת האפליקציה (ברירת מחדל): הערימה הראשית שבה האפליקציה מקצה זיכרון.
  • ערימת תמונות: קובץ האימג' לאתחול המערכת, שמכיל מחלקות שנטענות מראש בזמן האתחול. ההקצאות כאן אף פעם לא זזות או נעלמות.
  • Zygote heap: ה-heap מסוג copy-on-write שממנו מתפצל תהליך האפליקציה במערכת Android.

בתפריט הנפתח של הסידור, בוחרים איך לסדר את ההקצאות:

  • סידור לפי מחלקה (ברירת מחדל): קיבוץ כל ההקצאות לפי שם המחלקה.
  • סידור לפי חבילה: כל ההקצאות מקובצות לפי שם החבילה.

אתם יכולים להשתמש בתפריט הנפתח של הכיתות כדי לסנן לפי קבוצות של כיתות:

  • כל המחלקות (ברירת המחדל): הצגת כל המחלקות, כולל אלה מספריות ומיחסי תלות.
  • הצגת דליפות של פעילות/קטע: הצגת מחלקות שגורמות לדליפות זיכרון.
  • Show project classes: הצגת המחלקות שהוגדרו בפרויקט בלבד.

לוחצים על שם של כיתה כדי לפתוח את החלונית מופע. כל מכונה ברשימה כוללת את הפרטים הבאים:

  • ‫Depth: המספר הקצר ביותר של דילוגים מכל שורש GC למכונה שנבחרה.
  • גודל מקורי: הגודל של המופע הזה בזיכרון המקורי. העמודה הזו מוצגת רק ב-Android מגרסה 7.0 ואילך.
  • נפח בסיסי (shallow size): הגודל של המופע הזה בזיכרון של Java.
  • הנפח שמתפנה (retained size): נפח הזיכרון שהמופע הזה שולט בו (לפי עץ הדומיננטיות).

לוחצים על מופע כדי להציג את Instance Details, כולל Fields ו-References. סוגי השדות וההפניות הנפוצים הם סוגים מובְנים , מערכים , וסוגי נתונים פרימיטיביים ב-Java. לוחצים לחיצה ימנית על שדה או על הפניה כדי לעבור למופע או לשורה המשויכים בקוד המקור.

  • שדות: כל השדות במופע הזה.
  • ‫References: הצגת כל ההפניות לאובייקט שמודגש בכרטיסייה Instance.
התצוגות Instances,‏ Fields ו-References בחלון של הכלי תמונת מצב של הזיכרון.

זיהוי מפות סיביות כפולות

החל מ-Android Studio Narwhal 4, אפשר גם לזהות מפות סיביות מיותרות בתצוגה Heap Dump (תמונת מצב של הזיכרון).

כך אפשר למצוא אותם:

  1. פותחים את הכרטיסייה Profiler ב-Android Studio.
  2. לוחצים על Heap Dump (או על Analyze Memory Usage) ואז על סמל ההקלטה כדי ליצור תמונת מצב של הזיכרון הנוכחי של האפליקציה.
  3. מחפשים בתוצאות הניתוח את משולש האזהרה הצהוב ⚠️, שבו Android Studio משתמש כדי לסמן מפות סיביות כפולות שמאוחסנות כמה פעמים.
    • אפשר גם לעבור לכותרת של כלי הפרופיילינג, לבחור באפשרות Filter by:‎ (סינון לפי:) ולבחור בהגדרה Duplicate Bitmaps (מפות סיביות כפולות).
  4. לוחצים על כל רשומה שסומנה כדי לפתוח את החלונית Bitmap Preview, שבה אפשר לראות בדיוק איזו תמונה היא הגורם לבעיה.
  5. השתמשו באישור החזותי הזה כדי לאתר את לוגיקת הטעינה המיותרת בקוד שלכם ולהטמיע אסטרטגיית שמירה במטמון טובה יותר.
מחפשים מפות סיביות כפולות באמצעות משולש האזהרה הצהוב ⚠️.

איך מוצאים דליפות זיכרון

כדי לסנן במהירות את המחלקות שעשויות להיות משויכות לדליפות זיכרון, פותחים את התפריט הנפתח של המחלקות ובוחרים באפשרות Show activity/fragment leaks. ב-Android Studio מוצגות מחלקות שמעידות על דליפות זיכרון במופעים של Activity ו-Fragment באפליקציה.

כדי לחפש דליפות זיכרון באופן ידני, עוברים על רשימות המחלקות והמופעים ומחפשים אובייקטים עם הנפח שמתפנה גדול. מחפשים דליפות זיכרון שנגרמו בגלל אחת מהסיבות הבאות:

  • הפניות לטווח ארוך אל Activity או אל Context שעלולות להדליף את תרשים הקומפוזיציה של Compose שמתארח (כמו ComposeView והרכיבים הניתנים לשילוב שלו).
  • זליגה של אובייקטים של מצב ב-Jetpack Compose‏ (MutableState), מאחסני מצב או פונקציות למבדה שמתעדות את Context.
  • שכחתם לנקות מאזינים או משקיפים בבלוק onDispose של DisposableEffect.
  • מחלקות פנימיות לא סטטיות, כמו Runnable, שיכולות להכיל מופע של Activity.
  • מטמונים ששומרים אובייקטים יותר זמן מהנדרש.

כשמוצאים דליפות זיכרון פוטנציאליות, משתמשים בכרטיסיות Fields ו-References בקטע Instance Details כדי לעבור למכונה או לשורת קוד המקור הרלוונטית.

הפעלת דליפות זיכרון לצורך בדיקה

כדי לנתח את השימוש בזיכרון, כדאי להעמיס על קוד האפליקציה ולנסות לאלץ דליפות זיכרון. אחת הדרכים לגרום לדליפות זיכרון באפליקציה היא לתת לה לפעול במשך זמן מה לפני שבודקים את ה-heap. זליגות זיכרון יכולות להגיע לחלק העליון של ההקצאות בערימה. עם זאת, ככל שזליגת הזיכרון קטנה יותר, כך צריך להפעיל את האפליקציה למשך זמן ארוך יותר כדי לראות אותה.

אפשר גם להפעיל דליפת זיכרון באחת מהדרכים הבאות:

  • מסובבים את המכשיר ממצב לאורך למצב לרוחב ובחזרה כמה פעמים, בזמן שהאפליקציה נמצאת במצבי פעילות שונים. סיבוב המכשיר יכול לגרום לאפליקציה לדלוף Activity (וכתוצאה מכך גם את עץ ה-UI של Compose שמתארח בו ואת עצי המצב המשויכים) אם האפליקציה מכילה הפניה ל-Activity או ל-Context בתוך פעולות אסינכרוניות או מאחסני מצב.
  • לעבור בין האפליקציה שלכם לאפליקציה אחרת כשהן במצבי פעילות שונים. לדוגמה, עוברים למסך הבית ואז חוזרים לאפליקציה.

ייצוא וייבוא של הקלטת תמונת מצב של הזיכרון

אתם יכולים לייצא ולייבא קובץ של תמונת מצב של הזיכרון מהכרטיסייה Past Recordings בכלי ליצירת פרופילים. ‫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

מקורות מידע נוספים