אופטימיזציה של הזיכרון היא חיונית כדי לספק חוויות משחק יציבות עם ביצועים גבוהים ב-Android. במדריך הזה מוסבר למה יעילות הזיכרון חשובה, איך מערכת ההפעלה Android מנהלת את מגבלות הזיכרון של התהליך ואוכפת אותן, ומהם ערכי הסף החדשים שמוצגים ב-Google Play Console כדי לעזור לכם לעקוב אחרי האיכות הטכנית של המשחק ולשפר אותה.
החשיבות של אופטימיזציה של הזיכרון
אופטימיזציה של הזיכרון של המשחק חיונית כדי לשמר את השחקנים, להרחיב את התאימות למכשירים ולעמוד בתקני האיכות של הפלטפורמה:
- מניעת הפעלה במצב התחלתי (cold start) (חוויית שימוש ושימור): כששחקן עובר באופן זמני מהמשחק (למשל, כדי לענות על התראה או לבדוק הודעה), מערכת ההפעלה מעבירה את תהליך המשחק לרקע. אם הזיכרון שבשימוש של המשחק ברקע גבוה מדי, המערכת Low Memory Killer (LMK) נותנת עדיפות לסיום תהליך המשחק כדי לפנות RAM למשימות בחזית. בפעם הבאה שהמשתמש יחזור למשחק, במקום חזרה חלקה ומהירה למשחק, המשחק יצטרך לעבור הפעלה ארוכה של הפעלה במצב התחלתי (cold start) – טעינה מחדש מלאה של נכסי גרפיקה כבדים, אודיו וקבצים בינאריים של מנוע המשחק מהאחסון. שמירה על שימוש נמוך בזיכרון ברקע מונעת את הסגירות השקטות האלה של הרקע, שומרת על מצב המשתמש ומבטיחה שהשחקנים יוכלו להמשיך את הסשן שלהם באופן מיידי. לפרטים נוספים על ההתנהגות של LMK במערכת, אפשר לעיין במדריך Android Vitals - Low memory killers.
- יציבות המערכת והמכשיר: שימוש לא יעיל בזיכרון ודליפות זיכרון פוגעים בבריאות הכללית של המערכת. כשזיכרון המערכת מוגבל, המערכת נתונה ללחץ חזק שגורם לירידה בקצב הפריימים, לגמגום בממשק המשתמש ולשיבושים באודיו. אם העומס על הזיכרון חמור מדי, מנגנון ה-LMK (הפסקת תהליכים בגלל מחסור בזיכרון) של המערכת מפסיק באופן אגרסיבי תהליכים ברקע, וכתוצאה מכך יישומים אחרים נאלצים להתמודד עם הפעלה איטית של התהליכים ועם אובדן של מצב המשתמש כשמפעילים עוברים בין משימות.
- סיום תהליכים ברמת הפלטפורמה: החל מ-Android 17 (רמת API 37), המערכת יוזמת יותר פעולות לסיום תהליכים שצורכים יותר מדי זיכרון. אם טביעת הרגל של המשחק גדולה מדי, מערכת ההפעלה יכולה להפסיק את התהליך שלו בפתאומיות בלי ליצור דוח קריסות רגיל.
- תאימות המכשיר: למרות שמכשירי הדגל כוללים זיכרון RAM בנפח 12GB עד 16GB, חלק גדול מקהל הגיימרים העולמי משתמש במכשירים עם זיכרון RAM בנפח 4GB או 6GB. ניהול נכון של הזיכרון מבטיח שהמשחק יישאר נגיש ויגיב בכל רמות החומרה, בלי שיהיה צורך בחבילות נפרדות ומורכבות של נכסים.
הסבר על הזיכרון ב-Android
כדי לתכנן אסטרטגיות יעילות לניהול תקציב הזיכרון, המפתחים צריכים להבין איך פלטפורמת Android מנהלת את הזיכרון הפיזי ואיך היא מודדת את טביעת הרגל הפעילה של המשחק.
מושגי ליבה בנושא זיכרון ב-Android
למידע על מושגי יסוד בנושא ניהול זיכרון ברמת הפלטפורמה, אפשר לעיין במסמכי התיעוד הרשמיים בנושא סקירה כללית על ניהול זיכרון. במקור הזה מוסבר על ארבעה תחומים בארכיטקטורה:
- סקירה כללית של הזיכרון: מערכת Android משתמשת בהחלפה (paging) ובמיפוי זיכרון (mmap) כדי לנהל את ה-RAM. הוא לא תומך בקובץ החלפה מסורתי בדיסק, אלא מסתמך על דחיסת דפים (באמצעות zRAM) ועל שחזור דפים כדי לפנות זיכרון פיזי.
- הקצאת זיכרון בין תהליכים: מערכת Android משתפת את ה-RAM בכל המערכת. היא מקצה ערימות ספציפיות להרצה של מכונה וירטואלית של Dalvik או ART, ומאפשרת לסביבות פיתוח מקומיות (כמו מנועי משחקים של C++) לבקש זיכרון מהערימה המקומית של המערכת.
- ניהול הזיכרון של האפליקציה: מערכת Android פועלת לפי מודל מרובה תהליכים, ולכן האפליקציות צריכות לעקוב באופן דינמי אחרי מצב מחזור החיים שלהן ולשחרר מרצונן משאבים מיותרים (כמו גרפיקה ומפות סיביות שלא נשמרו במטמון) כדי לשמור על תקינות המערכת.
- סקירה כללית של תהליכים ושרשורים: המערכת מסווגת תהליכים להיררכיה על סמך הנראות והחשיבות שלהם כפי שהמשתמשים תופסים אותם, וקובעת אילו תהליכים יישארו פעילים ואילו יסתיימו ראשונים בתנאים של זיכרון נמוך.
מדד הזיכרון הכולל שבשימוש
תהליך ההערכה של צריכת הזיכרון ב-Android 17 Memory Limiter ברמת הפלטפורמה מתבצע באמצעות Total Memory Footprint (טביעת רגל כוללת של הזיכרון) ולא באמצעות total resident size (גודל תושב כולל, RSS) או גודל הזיכרון הווירטואלי.
טביעת רגל כוללת בזיכרון = RSS אנונימי (RssAnon) + החלפה לא דחוסה (VmSwap)
כדי למנוע חריגה של משחקים מהמגבלות שנאכפות על ידי הפלטפורמה, המפתחים צריכים להבין בדיוק מה המדדים האלה מייצגים ברמת המערכת. מידע נוסף על המדדים האלה, על הקצאות של זיכרון RAM פיזי ועל אופן הטיפול בדפים שמגובים בקובץ זמין במאמר הסבר על מדדי RSS ו-Swap במדריך מעקב אחר השימוש בזיכרון.
מגבלות זיכרון
כדי לשמור על יציבות המערכת ולוודא שאפליקציות לא צורכות משאבים מוגזמים, פלטפורמת Android אוכפת מגבלות זיכרון על תהליכים שפועלים.
הגבלת הזיכרון ב-Android מגרסה 17 ואילך
ב-Android מגרסה 17 ואילך, מוגדרות מגבלות זיכרון מחמירות לכל אפליקציה באמצעות Linux cgroup v2, כדי למנוע מאפליקציות ספציפיות לגרום לחוסר יציבות במערכת כולה. פרטים נוספים על ההטמעה הטכנית זמינים במדריך בנושא הגבלת הזיכרון ב-AOSP ובמאמר Prioritizing Memory Efficiency: Essential Steps for Android 17 Blog.
- מנגנון: מגביל הזיכרון עוקב אחרי כל תהליכי האפליקציה ומקצה מגבלות באופן דינמי על סמך מצב מחזור החיים של התהליך:
- תהליכים גלויים (חזיתיים): תהליכי אפליקציה שמציגים כרגע ממשק משתמש צפויים להפעיל קבוצת משאבים גדולה יותר, והמגבלה שלהם גדולה יותר.
- תהליכים לא גלויים (ברקע או שירותים): תהליכים של אפליקציות שמבצעים פעולות פעילות בלי להציג ממשק משתמש מוגבלים לתקציב מצומצם יותר ומגביל יותר.
- מאפייני ליבה: השירות מסתמך על שני מאפיינים עיקריים:
-
memory.high: מגבלה רכה. אם חורגים מהערך הזה, ליבת המערכת מגבילה את התהליך ומנסה לשחזר זיכרון באופן אגרסיבי. החזרת הזיכרון יכולה לגרום לירידה בביצועים של המשחק. -
memory.swap.max: מגדיר מגבלה קשיחה על שטח ההחלפה או ה-zRAM שבו התהליך יכול להשתמש.
-
- התנהגות של סיום התהליך: אם תהליך ממשיך להקצות זיכרון אנונימי מעבר ל-
memory.highוממצה את קיבולת ההחלפה שלו, ההקצאות נכשלות ומערכת ההפעלה מסיימת את התהליך באופן שקט. הסיום הזה מתועד באמצעותApplicationExitInfoבקטע Memory Limiter exit reason (סיבת היציאה של מגביל הזיכרון) (זמין החל מ-Android 17, 26Q4).
מעקב אחרי השימוש בזיכרון
כדי לבצע אופטימיזציה יעילה של הזיכרון של המשחק, קודם צריך להבין איך פלטפורמת Android מודדת את טביעת הרגל שלו. ב-Android 17, המדד לאכיפת הזיכרון מתעדכן כדי לעקוב אחרי הסכום של RSS אנונימי (RssAnon) ושל החלפה לא דחוסה (VmSwap), לא כולל זיכרון שמוגדר כגיבוי לקובץ או זיכרון פרטי של GPU. במדריך הזה מוסבר איך להשתמש בכלים ברמת המערכת כמו Perfetto ו-meminfo, איך להטמיע ממשקי API לאבחון כמו ProfilingManager ו-onTrimMemory, ואיך לחלץ הקצאות מדויקות של זיכרון ב-Unity וב-Unreal Engine. הסבר על יצירת פרופיל מדויק של המשחק כדי להימנע מבעיות בביצועים שקשורות לסקר זיכרון בזמן ריצה (runtime) בשיטה המסורתית.
מידע נוסף מופיע במאמר בנושא מעקב אחרי השימוש בזיכרון.
אסטרטגיות לצמצום הזיכרון
מנועי משחקים מפשטים את הפיתוח בפלטפורמות שונות, אבל הטיפול בזיכרון שמוגדר כברירת מחדל בהם יכול להפעיל מגבלות זיכרון ברמת מערכת ההפעלה. בדף הזה מפורטים שלבים מעשיים לאופטימיזציה שמותאמים במיוחד ל-Unity ול-Unreal Engine. הסבר למה הסתמכות על onTrimMemory מבוסס Java עלולה לגרום לקיפאון ב-Unity – ואיך להשתמש במקום זאת בפונקציות קריאה חוזרת (callback) של מחזור החיים המקורי. בנוסף, תמצאו מידע על אופטימיזציות חשובות ברמת הנכס, כמו שימוש בדחיסת טקסטורה מסוג ASTC 8x8 והגדרת הסרת נכסים, כדי שהמשחק יפעל בצורה חלקה בכל רמות החומרה.