תיעוד הקצאות Java/Kotlin

תיעוד של הקצאות Java/Kotlin עוזר לזהות דפוסי זיכרון לא רצויים שעלולים לגרום לבעיות בביצועים. הכלי ליצירת פרופילים יכול להציג לכם את הפרטים הבאים לגבי הקצאות של אובייקטים:

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

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

איך מקליטים הקצאות של Java/Kotlin

כדי להקליט הקצאות של Java/Kotlin, בוחרים את המשימה Track Memory Consumption (Java/Kotlin Allocations) בכרטיסייה Home של הכלי ליצירת פרופילים. הערה: כדי להקליט הקצאות של Java/Kotlin, צריך אפליקציה שאפשר לבצע בה ניפוי באגים (אפשר להשתמש באפשרות Profiler: run 'app' as debuggable (complete data)).

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

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

סקירה כללית של הקצאות ב-Java/Kotlin

אחרי שמפסיקים את ההקלטה, מוצגים הפרטים הבאים:

  • ציר הזמן של האירועים מציג את מצבי הפעילות, אירועי קלט של המשתמש ואירועי סיבוב המסך.
  • ציר הזמן של השימוש בזיכרון מציג את המידע הבא. אפשר לבחור חלק מציר הזמן כדי לסנן לפי טווח זמן מסוים.
    • תרשים עמודות מוערם שמציג את נפח הזיכרון שכל קטגוריית זיכרון צורכת, כפי שמצוין בציר ה-Y בצד ימין ובמקרא הצבעים בחלק העליון.
    • קו מקווקו מציין את מספר האובייקטים שהוקצו, כפי שמצוין בציר ה-y בצד שמאל.
    • סמל לכל אירוע של איסוף אשפה.
  • בכרטיסייה טבלה מוצגת רשימה של כיתות. המספר הכולל הוא מספר ההקצאות בסוף טווח הזמן שנבחר (הקצאות פחות ביטולים), ולכן כדאי לנפות באגים בכיתות עם הערכים הכי גבוהים של המספר הכולל. אם אתם רוצים לפתור בעיות בכיתות על סמך הקצאות בשיא השימוש במהלך טווח הזמן שנבחר, כדאי למיין לפי הקצאות. באופן דומה, הגודל שנותר הוא גודל ההקצאות פחות גודל הביטולים בבייטים.
  • כשלוחצים על מחלקה ברשימה Table, החלונית Instance נפתחת עם רשימה של אובייקטים משויכים, כולל מתי הם הוקצו, מתי הם בוטלו והגודל הרדוד שלהם.
  • בכרטיסייה תצוגה חזותית מוצגת תצוגה מצטברת של כל האובייקטים במחסנית הקריאות במהלך טווח הזמן שנבחר. הוא מראה לכם בעצם כמה זיכרון כולל תופס מחסנית הקריאות עם המופעים שמוצגים. בשורה הראשונה מוצג שם השרשור. כברירת מחדל, האובייקטים מוערמים משמאל לימין לפי גודל ההקצאה. אפשר להשתמש בתפריט הנפתח כדי לשנות את הסדר.

  • משתמשים בתפריט הנפתח של הערימה כדי לסנן ערימות מסוימות. בנוסף למסננים שזמינים כשמבצעים צילום מצב של ה-heap, אפשר לסנן לפי מחלקות ב-heap של JNI, ה-heap שמראה איפה מוקצים ומשוחררים הפניות של Java Native Interface ‏ (JNI).

  • משתמשים בתפריט הנפתח 'סידור' כדי לבחור איך לסדר את ההקצאות. בנוסף להסידורים שזמינים כשמבצעים dump של ה-heap, אפשר לסדר לפי callstack.

איך הזיכרון נספר

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

  • Java: זיכרון מאובייקטים שהוקצו מקוד Java או Kotlin.
  • Native: זיכרון מאובייקטים שהוקצו מקוד C או C++.

    גם אם אתם לא משתמשים ב-C++ באפליקציה, יכול להיות שתראו כאן שימוש בזיכרון מקורי כי ה-framework של Android משתמש בזיכרון מקורי כדי לטפל במשימות שונות בשמכם, למשל כשמטפלים בנכסי תמונות ובגרפיקה אחרת – גם אם הקוד שכתבתם הוא ב-Java או ב-Kotlin.

  • גרפיקה: הזיכרון שמשמש לתורים של מאגר הגרפיקה כדי להציג פיקסלים במסך, כולל משטחי GL, טקסטורות GL ועוד. חשוב לדעת: זהו זיכרון משותף עם המעבד (CPU), ולא זיכרון GPU ייעודי.

  • Stack: הזיכרון שמשמש את ה-stacks המקוריים ואת ה-stacks של Java באפליקציה. בדרך כלל, הנתון הזה קשור למספר ה-threads שהאפליקציה מריצה.

  • קוד: הזיכרון שהאפליקציה משתמשת בו לקוד ולמשאבים, כמו DEX bytecode, קוד DEX שעבר אופטימיזציה או קומפילציה.‫so ספריות וגופנים.

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

  • הוקצו: מספר האובייקטים של Java או Kotlin שהוקצו על ידי האפליקציה. לא נכללים כאן אובייקטים שהוקצו ב-C או ב-C++.

בדיקת רשומת ההקצאה

כדי לבדוק את רשומת ההקצאה, פועלים לפי השלבים הבאים:

  1. מעיינים ברשימת הכיתות בכרטיסייה Table כדי למצוא אובייקטים עם ערכים גדולים באופן חריג של Allocations או Total Count (בהתאם למה שמנסים לבצע אופטימיזציה עבורו) שאולי יש בהם דליפה.
  2. בחלונית תצוגת מופע, לוחצים על מופע. בהתאם למה שרלוונטי לאותו מופע, נפתח הכרטיסייה Fields או Allocation Call Stack. אפשר להשתמש במידע בכרטיסיות Fields או Allocation Call Stack כדי לקבוע אם יש צורך במופעים או שמדובר בשכפולים מיותרים.

לוחצים לחיצה ימנית על רשומה כלשהי ברשימה כדי לדלג לקוד המקור הרלוונטי.

הצגת הפניות גלובליות ל-JNI

Java Native Interface (JNI) הוא מסגרת שמאפשרת לקוד Java ולקוד מקורי לקרוא אחד לשני. ההפניות ל-JNI מנוהלות באופן ידני על ידי הקוד המקורי, ולכן יכולות להתרחש בעיות, כולל הבעיות הבאות:

  • אובייקטים של Java שמשמשים קוד מקורי נשמרים פעילים למשך זמן ארוך מדי.
  • יכול להיות שחלק מהאובייקטים ב-Java heap לא יהיו נגישים אם הפניה ל-JNI תימחק בלי שנמחק אותה קודם באופן מפורש.
  • הגעתם למגבלת ההפניות של JNI.

כדי לפתור בעיות כאלה, בוחרים באפשרות View JNI heap (הצגת ערימת JNI) בכלי ליצירת פרופילים כדי לעיין בכל ההפניות הגלובליות של JNI ולסנן אותן לפי סוגי Java ומחסניות קריאה מקוריות. לוחצים לחיצה ימנית על שדה מופע בכרטיסייה Fields (שדות) ובוחרים באפשרות Go to instance (מעבר למופע) כדי לראות את מחסנית הקריאות הרלוונטית להקצאה.

בכרטיסייה Allocation Call Stack (ערימת קריאות להקצאת זיכרון) אפשר לראות איפה מוקצים ומשוחררים הפניות ל-JNI בקוד.

מידע נוסף על JNI זמין במאמר טיפים לשימוש ב-JNI.