בפלטפורמת Android, המערכת מנסה להשתמש בכמה שיותר זיכרון מערכת (RAM) ומבצעת אופטימיזציות שונות של הזיכרון כדי לפנות מקום כשצריך. האופטימיזציות האלה עלולות להשפיע לרעה על המשחק, למשל להאט אותו או לגרום לקריסה שלו. מידע נוסף על האופטימיזציות האלה זמין בנושא הקצאת זיכרון בין תהליכים.
בדף הזה מוסבר איך אפשר למנוע מצבים של זיכרון נמוך שמשפיעים על המשחק.
תגובה ל-onTrimMemory()
המערכת משתמשת ב-onTrimMemory()
כדי להודיע לאפליקציה על אירועים במחזור החיים שלה שמהווים הזדמנות טובה לאפליקציה לצמצם באופן יזום את השימוש שלה בזיכרון, וכך להימנע מהפסקת התהליכים שלה על ידי הפסקת תהליכים בגלל מחסור בזיכרון (LMK), כדי לפנות זיכרון לשימוש של אפליקציות אחרות.
אם האפליקציה נסגרת ברקע, בפעם הבאה שהמשתמש יפעיל אותה, הוא יחווה הפעלה קרה איטית. אפליקציות שמפחיתות את השימוש בזיכרון כשהן עוברות לרקע, פחות סביר שייפסק הפעילות שלהן ברקע.
כשמגיבים לאירועי חיתוך, מומלץ לבטל הקצאות גדולות של זיכרון שלא נדרשות באופן מיידי, ואפשר לשחזר אותן לפי דרישה. לדוגמה, אם באפליקציה יש מטמון של מפות סיביות שפוענחו מתמונות דחוסות שמאוחסנות באופן מקומי, כדאי לעיתים קרובות לצמצם או לנקות את המטמון הזה בתגובה ל-TRIM_MEMORY_UI_HIDDEN
.
Kotlin
class MainActivity : AppCompatActivity(), ComponentCallbacks2 { override fun onTrimMemory(level: Int) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } }
Java
public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 { public void onTrimMemory(int level) { switch (level) { if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Release memory related to UI elements, such as bitmap caches. } if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Release memory related to background processing, such as by // closing a database connection. } } } }
C#
using UnityEngine; using System.Collections; using System.Collections.Generic; class LowMemoryTrigger : MonoBehaviour { private void Start() { Application.lowMemory += OnLowMemory; } private void OnLowMemory() { // Respond to low memory condition (e.g., Resources.UnloadUnusedAssets()) } }
הקצאת תקציבי זיכרון בצורה שמרנית
כדאי להקצות זיכרון בצורה שמרנית כדי למנוע מצב של חוסר זיכרון. הנה כמה דברים שכדאי לקחת בחשבון:
- גודל ה-RAM הפיזי: משחקים לרוב משתמשים ב-¼ עד ½ מכמות ה-RAM הפיזי במכשיר.
- גודל zRAM מקסימלי: יותר zRAM פירושו שלמשחק יש פוטנציאל להקצאת יותר זיכרון. הסכום הזה יכול להשתנות בהתאם למכשיר. כדי למצוא את הערך הזה, מחפשים את
SwapTotal
ב-/proc/meminfo
. - שימוש בזיכרון של מערכת ההפעלה: במכשירים שבהם מוקצה יותר זיכרון RAM לתהליכי המערכת, נשאר פחות זיכרון למשחק. המערכת מפסיקה את התהליך של המשחק לפני שהיא מפסיקה את התהליכים של המערכת.
- שימוש בזיכרון של אפליקציות מותקנות: כדאי לבדוק את המשחק במכשירים שמותקנות בהם הרבה אפליקציות. אפליקציות של רשתות חברתיות ואפליקציות צ'אט צריכות לפעול כל הזמן, והן משפיעות על כמות הזיכרון הפנוי.
אם אתם לא יכולים להתחייב לתקציב זיכרון שמרני, כדאי לנקוט גישה גמישה יותר. אם המערכת נתקלת בבעיות של זיכרון נמוך, צריך להקטין את כמות הזיכרון שבה נעשה שימוש במשחק. לדוגמה, הקצאת טקסטורות ברזולוציה נמוכה יותר או אחסון של פחות הצללות בתגובה ל-onTrimMemory()
. הגישה הדינמית הזו להקצאת זיכרון דורשת יותר עבודה מהמפתח, במיוחד בשלב עיצוב המשחק.
איך להימנע משימוש יתר בדיסק
החלפת דפים תכופה מתרחשת כשזיכרון פנוי נמוך, אבל לא נמוך מספיק כדי להפסיק את המשחק.
במצב הזה, דפדפן kswapd
השתמש מחדש בדפים שהמשחק עדיין צריך, ולכן הוא מנסה לטעון מחדש את הדפים מהזיכרון. אין מספיק מקום, ולכן הדפים ממשיכים להיות מוחלפים (החלפה רציפה).
System tracing מדווח על המצב הזה כשרשור שבו הפונקציה kswapd
פועלת באופן רציף.
אחד מהתסמינים של thrashing הוא זמני פריימים ארוכים – יכול להיות שנייה או יותר. כדי לפתור את הבעיה, צריך להקטין את תצרוכת הזיכרון של המשחק.
שימוש בכלים הזמינים
ב-Android יש אוסף של כלים שעוזרים להבין איך המערכת מנהלת את הזיכרון.
Meminfo
הכלי הזה אוסף נתונים סטטיסטיים על הזיכרון כדי להראות כמה זיכרון PSS הוקצה והקטגוריות שבהן נעשה בו שימוש.
מדפיסים את נתוני הסטטיסטיקה של meminfo באחת מהדרכים הבאות:
- משתמשים בפקודה
adb shell dumpsys meminfo package-name
. - משתמשים בקריאה
MemoryInfo
מ-Android Debug API.
הנתון הסטטיסטי PrivateDirty
מציג את כמות ה-RAM בתהליך שלא ניתן להעביר לדף בדיסק ולא משותף עם תהליכים אחרים. רוב הסכום הזה הופך לזמין למערכת כשהתהליך הזה מסתיים.
נקודות מעקב בזיכרון
נקודות מעקב של זיכרון עוקבות אחרי כמות הזיכרון של RSS שבה נעשה שימוש במשחק. חישוב השימוש בזיכרון של RSS מהיר בהרבה מחישוב השימוש בזיכרון של PSS. החישוב של RSS מהיר יותר, ולכן הוא מציג רמת פירוט גבוהה יותר של השינויים בגודל הזיכרון, כדי למדוד בצורה מדויקת יותר את השימוש המקסימלי בזיכרון. לכן קל יותר לזהות שיאים שעלולים לגרום למשחק לאבד זיכרון.
Perfetto ומעקבים ארוכים
Perfetto הוא חבילת כלים לאיסוף מידע על הביצועים והזיכרון במכשיר ולהצגתו בממשק משתמש מבוסס-אינטרנט. הוא תומך במעקב ארוך ככל שנדרש, כך שאפשר לראות איך ה-RSS משתנה לאורך זמן. אפשר גם להריץ שאילתות SQL על הנתונים שנוצרים כדי לעבד אותם אופליין. מפעילים מעקבים ארוכים מאפליקציית מעקב המערכת. מוודאים שהקטגוריה memory:Memory מופעלת למעקב.
heapprofd
heapprofd
הוא כלי למעקב אחרי הזיכרון, והוא חלק מ-Perfetto. הכלי הזה יכול לעזור לכם למצוא דליפות זיכרון. הוא מציג את המקומות שבהם הוקצה זיכרון באמצעות malloc
. אפשר להפעיל את heapprofd
באמצעות סקריפט Python, ומכיוון שהתקורה של הכלי נמוכה, הוא לא משפיע על הביצועים כמו כלים אחרים, כמו Malloc Debug.
דוח על באג
bugreport
הוא כלי לרישום ביומן שמאפשר לגלות אם המשחק קרס כי נגמר לו הזיכרון. הפלט של הכלי מפורט הרבה יותר מזה שמתקבל באמצעות logcat. הוא שימושי לניפוי באגים בזיכרון כי הוא מראה אם המשחק קרס בגלל שנגמר לו הזיכרון או אם הוא הופסק על ידי LMK.
מידע נוסף זמין במאמר בנושא יצירה וקריאה של דוחות באגים.