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