פלטפורמת Android פועלת על בסיס ההנחה שזיכרון פנוי הוא זיכרון מבוזבז. הוא מנסה להשתמש בכל הזיכרון הזמין בכל רגע. לדוגמה, המערכת שומרת אפליקציות בזיכרון אחרי שהן נסגרות, כדי שהמשתמש יוכל לחזור אליהן במהירות. לכן, במכשירי Android יש לרוב מעט מאוד זיכרון פנוי. ניהול הזיכרון חיוני להקצאת זיכרון נכונה בין תהליכי מערכת חשובים לבין אפליקציות רבות של משתמשים.
בדף הזה נסביר את עקרונות הבסיס של הקצאת זיכרון ב-Android למערכת ולאפליקציות של משתמשים. בנוסף, נסביר איך מערכת ההפעלה מגיבה למצבים של זיכרון נמוך.
סוגי זיכרונות
מכשירי Android מכילים שלושה סוגים שונים של זיכרון: RAM, zRAM ואחסון. חשוב לשים לב שגם המעבד וגם ה-GPU ניגשים לאותו זיכרון RAM.
איור 1. סוגי זיכרון – RAM, zRAM ואחסון
זיכרון RAM הוא סוג הזיכרון המהיר ביותר, אבל בדרך כלל הוא מוגבל בגודל. בדרך כלל, במכשירים מתקדמים יש את הכמויות הכי גדולות של זיכרון RAM.
zRAM היא מחיצה של RAM שמשמשת כשטח החלפה. הכול נדחס כשמכניסים אותו ל-zRAM, ואז הוא נפרס כשמעתיקים אותו מ-zRAM. החלק הזה של ה-RAM גדל או קטן ככל שדפים מועברים אל zRAM או מוצאים ממנו. יצרני המכשירים יכולים להגדיר את הגודל המקסימלי.
האחסון מכיל את כל הנתונים הקבועים, כמו מערכת הקבצים וקוד האובייקט הכלול של כל האפליקציות, הספריות והפלטפורמה. נפח האחסון גדול בהרבה מנפח הזיכרון משני הסוגים האחרים. ב-Android, האחסון לא משמש כמרחב החלפה כמו ביישומים אחרים של Linux, כי כתיבה תכופה עלולה לגרום לבלאי בזיכרון הזה ולקצר את משך החיים של אמצעי האחסון.
דפי זיכרון
ה-RAM מחולק לדפים. בדרך כלל כל דף הוא בנפח 4KB של זיכרון.
הדפים נחשבים free או used. דפים פנויים הם זיכרון RAM שלא נעשה בו שימוש. הזיכרון RAM שבו המערכת משתמשת באופן פעיל, ומקובץ לקטגוריות הבאות:
- במטמון: זיכרון שמגובה על ידי קובץ באחסון (לדוגמה, קוד או קבצים שמופים לזיכרון). יש שני סוגים של זיכרון מטמון:
- פרטי: בבעלות תהליך אחד ולא משותף
- נקי: עותק לא משונה של קובץ באחסון, אפשר למחוק אותו על ידי
kswapdכדי להגדיל את הזיכרון הפנוי - מלוכלך: עותק של הקובץ ששונה באחסון; אפשר להעביר אותו ל-zRAM או לדחוס אותו ב-zRAM באמצעות
kswapdכדי להגדיל את הזיכרון הפנוי
- נקי: עותק לא משונה של קובץ באחסון, אפשר למחוק אותו על ידי
- משותף: בשימוש של כמה תהליכים
- פרטי: בבעלות תהליך אחד ולא משותף
- אנונימי: זיכרון לא מגובה על ידי קובץ באחסון (לדוגמה, הוקצה על ידי
mmap()עם ההגדרה של הדגלMAP_ANONYMOUS)- מלוכלך: אפשר להעביר או לדחוס ב-zRAM על ידי
kswapdכדי להגדיל את הזיכרון הפנוי
- מלוכלך: אפשר להעביר או לדחוס ב-zRAM על ידי
היחס בין הדפים הפנויים לדפים שבשימוש משתנה לאורך זמן, כי המערכת מנהלת באופן פעיל את ה-RAM. המושגים שמוצגים בקטע הזה חשובים לניהול מצבים של זיכרון נמוך. בקטע הבא במסמך הזה מוסבר על כך בפירוט.
ניהול זיכרון נמוך
ב-Android יש שני מנגנונים עיקריים להתמודדות עם מצבים של זיכרון נמוך: שד החלפה של ליבת המערכת (kernel) ו-low-memory killer.
דימון להחלפת ליבה
הדמון להחלפת ליבה (kswapd) הוא חלק מליבת Linux, והוא ממיר זיכרון בשימוש לזיכרון פנוי. הדמון מופעל כשהזיכרון הפנוי במכשיר נמוך. ליבת Linux שומרת על ספי זיכרון פנוי נמוכים וגבוהים.
כשהזיכרון הפנוי יורד מתחת לסף הנמוך, kswapd מתחיל לפנות זיכרון. כשהזיכרון הפנוי מגיע לסף הגבוה, kswapd מפסיק לפנות זיכרון.
kswapd יכול למחוק דפים נקיים כדי לפנות מקום כי הם מגובים באחסון ולא בוצעו בהם שינויים. אם תהליך מנסה לגשת לדף נקי שנמחק, המערכת מעתיקה את הדף מהאחסון ל-RAM. הפעולה הזו נקראת החלפה לפי דרישה.
איור 2. דף נקי, שגובה על ידי אחסון, נמחק
kswapd יכול להעביר דפים פרטיים עם נתונים מלוכלכים ודפים אנונימיים עם נתונים מלוכלכים שנשמרו במטמון אל zRAM, שם הם נדחסים. כך מפנים מקום בזיכרון ה-RAM (דפים פנויים). אם תהליך מנסה לגשת לדף נתונים מלוכלכים (dirty) ב-zRAM, הדף עובר דה-קומפרסיה ומועבר בחזרה ל-RAM. אם התהליך שמשויך לדף דחוס מופסק, הדף נמחק מ-zRAM.
אם כמות הזיכרון הפנוי יורדת מתחת לסף מסוים, המערכת מתחילה להרוג תהליכים.
איור 3. דף מלוכלך הועבר ל-zRAM ונדחס
הפסקת תהליכים בגלל מחסור בזיכרון
במקרים רבים, kswapd לא יכול לפנות מספיק זיכרון למערכת. במקרה כזה, המערכת משתמשת ב-onTrimMemory() כדי להודיע לאפליקציה שהזיכרון עומד להיגמר ושהיא צריכה לצמצם את ההקצאות שלה. אם זה לא מספיק, ליבת המערכת מתחילה להרוג תהליכים כדי לפנות זיכרון. הפעולה הזו מתבצעת באמצעות LMK (הפסקת תהליכים בגלל מחסור בזיכרון).
כדי להחליט איזה תהליך להרוג, LMK משתמש בציון 'אין זיכרון פנוי' שנקרא oom_adj_score כדי להגדיר סדר עדיפויות לתהליכים הפועלים. תהליכים עם ציון גבוה מושבתים קודם. האפליקציות ברקע הן הראשונות שנסגרות, ותהליכי המערכת הם האחרונים שנסגרים. בטבלה הבאה מפורטות קטגוריות הניקוד של LMK, מהגבוהה לנמוכה. פריטים בקטגוריה עם הניקוד הכי גבוה, בשורה הראשונה, יושמדו קודם:
איור 4. תהליכי Android, עם ציונים גבוהים בחלק העליון וציונים נמוכים בחלק התחתון
אלה תיאורים של הקטגוריות השונות בטבלה שלמעלה:
אפליקציות ברקע: אפליקציות שהופעלו בעבר ולא פעילות כרגע. ה-LMK יפסיק קודם את האפליקציות שפועלות ברקע, החל מהאפליקציה עם
oom_adj_scoreהכי גבוה.האפליקציה הקודמת: האפליקציה שהייתה ברקע והייתה בשימוש לאחרונה. לאפליקציה הקודמת יש עדיפות גבוהה יותר (ציון נמוך יותר) מאשר לאפליקציות ברקע, כי סביר יותר שהמשתמש יעבור אליה מאשר לאחת מהאפליקציות ברקע.
אפליקציית Home: זו אפליקציית מרכז האפליקציות. אם תסגרו אותה, הטפט ייעלם.
שירותים: שירותים מופעלים על ידי אפליקציות ועשויים לכלול סנכרון או העלאה לענן.
אפליקציות שניתן להבחין בהן: אפליקציות שלא פועלות בחזית אבל המשתמש יכול להבחין בהן בדרך כלשהי, למשל אפליקציות שמריצות תהליך חיפוש שמציג ממשק משתמש קטן או אפליקציות שמנגנות מוזיקה.
אפליקציה שפועלת בחזית: האפליקציה שנמצאת כרגע בשימוש. הפסקת הפעולה של האפליקציה שפועלת בחזית נראית כמו קריסה של האפליקציה, וזה עלול להצביע למשתמש על כך שמשהו לא תקין במכשיר.
קבועים (שירותים): אלה שירותי ליבה של המכשיר, כמו טלפוניה ו-Wi-Fi.
מערכת: תהליכי מערכת. במהלך סגירת התהליכים האלה, יכול להיות שהטלפון יופעל מחדש.
מקומי: תהליכים ברמה נמוכה מאוד שהמערכת משתמשת בהם (לדוגמה,
kswapd).
יצרני המכשירים יכולים לשנות את ההתנהגות של התכונה 'כדאי לדעת'.
חישוב הזיכרון שבשימוש
הליבה עוקבת אחרי כל דפי הזיכרון במערכת.
איור 5. דפים שמשמשים תהליכים שונים
כשקובעים כמה זיכרון נמצא בשימוש של אפליקציה, המערכת צריכה לקחת בחשבון דפים משותפים. אפליקציות שניגשות לאותו שירות או לאותה ספריה ישתפו דפי זיכרון. לדוגמה, יכול להיות ש-Google Play Services ואפליקציית משחק ישתמשו באותו שירות מיקום. לכן קשה לקבוע כמה זיכרון שייך לשירות באופן כללי וכמה שייך לכל אפליקציה.
איור 6. דפים ששותפו על ידי שתי אפליקציות (באמצע)
כדי לקבוע את הזיכרון שבשימוש של אפליקציה, אפשר להשתמש בכל אחת מהמדדים הבאים:
- גודל קבוצת התושבים (RSS): מספר הדפים המשותפים והלא משותפים שבהם נעשה שימוש באפליקציה
- גודל קבוצה יחסי (PSS): מספר הדפים שלא משותפים שבהם נעשה שימוש באפליקציה, וחלוקה שווה של הדפים המשותפים (לדוגמה, אם שלושה תהליכים משתפים 3MB, כל תהליך מקבל 1MB ב-PSS)
- גודל קבוצה ייחודי (USS): מספר הדפים הלא משותפים שבהם נעשה שימוש באפליקציה (דפים משותפים לא נכללים)
המדד PSS שימושי למערכת ההפעלה כשהיא רוצה לדעת בכמה זיכרון משתמשים כל התהליכים, כי הדפים לא נספרים כמה פעמים. חישוב ה-PSS לוקח הרבה זמן כי המערכת צריכה לקבוע אילו דפים משותפים וכמה תהליכים משתפים אותם. ב-RSS אין הבחנה בין דפים משותפים לבין דפים לא משותפים (מה שהופך את החישוב למהיר יותר), והוא מתאים יותר למעקב אחרי שינויים בהקצאת הזיכרון.
מקורות מידע נוספים
- סקירה כללית על ניהול הזיכרון
- תהליכים ומחזור החיים של האפליקציה
- הסבר על השימוש בזיכרון ב-Android – מצגת מ-Google I/O
- זיכרון ומשחקים ב-Android – מצגת מ-Google I/O
- Android low memory killer daemon
מומלץ בשבילכם
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- זמן ההפעלה של האפליקציה