Android 17 ואילך כולל את Memory Limiter, שירות מערכת שמנטר ומגביל את השימוש בזיכרון של תהליכי אפליקציות באמצעות Linux cgroup v2. התכונה 'הגבלת הזיכרון' מונעת מאפליקציות בודדות לצרוך זיכרון מערכת באופן לא פרופורציונלי, וכך מפחיתה את העומס הכולל על הזיכרון ומונעת הפסקות תהליכים אגרסיביות בגלל מחסור בזיכרון של תהליכים קריטיים שפועלים ברקע ושל תהליכים ששמורים במטמון.
מנגנון
הכלי Memory Limiter משולב עם Activity Manager Service (AMS) כדי לעקוב אחרי אירועים במחזור החיים של התהליך ושינויים במצב. הכלי Memory Limiter אוכף מגבלות זיכרון באמצעות ההיררכיה של cgroup v2 בליבת Linux.
כדי לתמוך במגביל הזיכרון, ליבת המכשיר צריכה להפעיל את cgroup v2 ואת בקר memory. השירות מסתמך באופן ספציפי על המאפיינים הבאים:
memory.high- מגבלה רכה. אם חורגים מהמגבלה, התהליך מוגבל והליבה מנסה לבצע שחזור פרואקטיבי של דפים מהתהליך הזה.
memory.swap.max- מגביל את הכמות המקסימלית של מרחב החלפה (כמו ZRAM) שהתהליך יכול להשתמש בה.
ההשפעה על אפליקציות
אפליקציות שפועלות במסגרת מגבלות הזיכרון שלהן לא מושפעות מהתכונה Memory Limiter.
כשאפליקציה חורגת מהמגבלה שלה memory.high, ליבת המערכת מפנה את הזיכרון הנקי של האפליקציה שמוגדר כקובץ, ומחליפה את הזיכרון האנונימי הלא פעיל שלה כדי שהאפליקציה לא תחרוג מהמגבלה. כתוצאה מהוצאת הדף והחלפתו, יכול להיות שיהיה ויסות זמני של הביצוע באפליקציה, אבל היא תמשיך לפעול.
במצבים קיצוניים – כמו דליפת זיכרון לא מבוקרת שבה האפליקציה ממשיכה להקצות זיכרון אנונימי אחרי שנגמר המקום בזיכרון הווירטואלי – האפליקציה לא מצליחה להקצות זיכרון והיא מסתיימת.
מעקב אחר תהליכים
כברירת מחדל, הכלי Memory Limiter עוקב אחרי תהליכי אפליקציות (UID >= 10000). תהליכי ליבה של המערכת פטורים כדי לשמור על יציבות חיונית של המערכת.
הכלי להגבלת הזיכרון מקצה מגבלות זיכרון על סמך מצב החשיפה של התהליך:
תהליכים גלויים: תהליכים שמארחים ממשק משתמש שגלוי למשתמש (כמו הפעילות שמוצגת כרגע או אינטראקציה פעילה). הצגת ממשק משתמש דורשת קבוצת עבודה גדולה יותר עבור צינורות עיבוד וגרפיקה, ולכן תהליכים גלויים מקבלים מגבלת זיכרון גדולה יותר.
תהליכים שלא נראים: תהליכים שמבצעים עבודה ברקע בלי להציג ממשק משתמש (כמו שירותים ברקע או מקלטי שידור). מכיוון שהם לא מעבדים רכיבי ממשק משתמש, הם פועלים במסגרת מגבלה מחמירה יותר.
בטבלה הבאה מפורט מיפוי של מצבי תהליך ספציפיים לסיווגים של מגבלות זיכרון:
| מצב התהליך | מגבלת זיכרון |
|---|---|
PERSISTENT | בלתי מוגבלת |
PERSISTENT_UI | בלתי מוגבלת |
TOP | גלוי |
BOUND_TOP | גלוי |
FOREGROUND_SERVICE | לא מוצג |
BOUND_FOREGROUND_SERVICE | לא מוצג |
IMPORTANT_FOREGROUND | גלוי |
IMPORTANT_BACKGROUND | לא מוצג |
TRANSIENT_BACKGROUND | לא מוצג |
BACKUP | לא מוצג |
SERVICE | לא מוצג |
RECEIVER | לא מוצג |
TOP_SLEEPING | גלוי |
HEAVY_WEIGHT | לא מוצג |
HOME | לא מוצג |
LAST_ACTIVITY | לא מוצג |
CACHED_ACTIVITY | מטמון |
CACHED_ACTIVITY_CLIENT | מטמון |
CACHED_RECENT | מטמון |
CACHED_EMPTY | מטמון |
במצב המאוחסן במטמון, התהליכים מוקפאים ואז מתבצעת דרישה חוזרת מקסימלית של משאבים.
אם תהליך חורג ממגבלת memory.high שהוקצתה לו, הכלי Memory Limiter מזהה את האירוע ויכול להפעיל פעולות אבחון, כמו צילום פרופיל זיכרון או רישום של אנומליה ב-statsd.
הגדרות אישיות
ב-Android מגרסה 17 ואילך, הכלי Memory Limiter מספק קובץ הגדרות ברירת מחדל סטנדרטי של הפלטפורמה במחיצת המערכת, ותומך בביטולים אופציונליים ספציפיים למכשיר במחיצה vendor:
- הגדרת ברירת המחדל של המערכת:
/system/etc/memory-limiter-config.xml - שינוי ספק (אופציונלי):
/vendor/etc/memory-limiter-config.xml
הכלי Memory Limiter בודק קודם אם יש /vendor/etc/memory-limiter-config.xml.
אם היא קיימת, היא משתמשת בהגדרות הספק. אחרת, היא חוזרת להגדרות ברירת המחדל של המערכת ב-/system/etc/memory-limiter-config.xml.
פורמט XML
קובץ התצורה פועל לפי הסכימה שמוגדרת ב-memory-limiter-config.xsd. הקובץ מגדיר כמה קבוצות של מגבלות, שמסודרות לפי הזיכרון הזמין. השירות בוחר את קבוצת המגבלות התואמת הגבוהה ביותר על סמך זיכרון ה-RAM הזמין במכשיר. כל ערכי הזיכרון מצוינים במביבייט (MiB).
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM minimums account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device (MemTotal >= 13GiB): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>13312</minimumRequiredMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device (MemTotal >= 10GiB): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>10240</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device (MemTotal >= 6.5GiB): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6656</minimumRequiredMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device (MemTotal >= 4.5GiB): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4608</minimumRequiredMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
<memVisible>2048</memVisible>
<memNotVisible>1024</memNotVisible>
<swapVisible>1024</swapVisible>
<swapNotVisible>1024</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version
- מספר שלם חיובי שמזהה את גרסת ההגדרה. הערך חייב להיות
1. minimumRequiredMemTotalהגודל המינימלי של המערכת
MemTotal(ב-MiB) שנדרש כדי שההגבלה הזו תחול. השירות משווה את הנתון הזה לזיכרון הכולל שזמין לפי הדיווח של ליבת המערכת ב-/proc/meminfo.הסבר על הקצאת זיכרון: הערך של הליבה
MemTotalמשקף את ה-DRAM הפיזי שאפשר לגשת אליו ממערכת ההפעלה אחרי שמנכים את הקצאת הזיכרון של החומרה והקושחה. הקצאת זיכרון RAM מראש מאפשרת להקצות זיכרון RAM לרכיבי חומרה ייעודיים כמו GPU, מודם פס בסיס, ISP של מצלמה, סביבות ביצוע מאובטחות והיפר-ויזורים. בדרך כלל, הרכיבים האלה צורכים בין 500 MiB ליותר מ-1 GiB של זיכרון פיזי.מכיוון שהערך של
MemTotalנמוך מהערך של ה-DRAM הפיזי שמפורסם במפרט המכשיר, הסף שלminimumRequiredMemTotalכולל מרווח לערכים האלה. לדוגמה, אם מגדירים טירגוט למכשירים עם נפח אחסון של 16GB, מצייניםminimumRequiredMemTotalשל13312(13GB) ולא16384(16GB). אם מגדירים טירגוט למכשירים עם נפח אחסון של 12GB, מציינים10240(10GB). כך אפשר לוודא שכל מכשיר מתאים לרמה המיועדת שלו, בלי קשר לשינויים בהקצאת המשאבים ברמת הלוח.memVisibleמגבלת הזיכרון הרכה (
memory.high) ב-MiB שחלה על תהליכים גלויים.memNotVisibleמגבלת הזיכרון הרכה (
memory.high) ב-MiB חלה על תהליכים שלא נראים.swapVisibleמגבלת מרחב ההחלפה (
memory.swap.max) ב-MiB שחלה על תהליכים גלויים.swapNotVisibleמגבלת אזור ההחלפה (
memory.swap.max) ב-MiB שחלה על תהליכים לא גלויים.
מגבלות סטנדרטיות בפלטפורמה
ב-Android 17 ואילך, הפלטפורמה מציינת מגבלות סטנדרטיות של זיכרון ושל swap ב-/system/etc/memory-limiter-config.xml ברמות נפוצות של זיכרון RAM פיזי:
| זיכרון RAM פיזי לטירגוט |
מינימום נדרש MemTotal (minimumRequiredMemTotal) |
מגבלת זיכרון גלויה (memVisible) |
מגבלת זיכרון לא גלויה (memNotVisible) |
מגבלת החלפה גלויה (swapVisible) |
מגבלת החלפה לא גלויה (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | 13,312 MiB (13 GiB) | 10,240 MiB (10 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) |
| 12 GB | 10,240 MiB (10 GiB) | 8,192 MiB (8 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) |
| 8 GB | 6,656 MiB (6.5 GiB) | 5,120 MiB (5 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) |
| 6 GB | 4,608 MiB (4.5 GiB) | 4,096 MiB (4 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) |
| 4 GB | 3,072 MiB (3 GiB) | 2,048 MiB (2 GiB) | 1,024 MiB (1 GiB) | 1,024 MiB (1 GiB) | 1,024 MiB (1 GiB) |
עקרונות של מגבלת זיכרון
ההגדרה של Memory Limiter מבוססת על העקרונות הבאים של הפלטפורמה:
עקביות במערכת האקולוגית ותאימות אפליקציות: מגבלות זיכרון עקביות במכשירים שונים מבטיחות ביצועי אפליקציה צפויים בכל מערכת האקולוגית של Android. האפליקציות מפותחות ונבדקות בהתאם לציפיות לגבי זיכרון סטנדרטי. שמירה על מגבלות אחידות בפלטפורמה מונעת הגבלות לא צפויות או סגירה מוקדמת של אפליקציות.
הקצאת משאבים יחסית: המגבלות של הפלטפורמה מכוילות בהתאם לקיבולת הזיכרון הפיזי (RAM):
- תהליכים גלויים: הוקצו בערך 1/2 עד 2/3 מזיכרון ה-RAM הפיזי הכולל כדי לתמוך בעומסי עבודה פעילים של ממשק משתמש, קומפוזיציה ורינדור.
- תהליכים לא גלויים: מוקצה בערך 1/4 עד 1/3 מזיכרון ה-RAM הפיזי הכולל למשימות ברקע.
החלה אוניברסלית: המגבלות חלות באופן אחיד על כל תהליכי האפליקציות במכשיר (UID >= 10000), כולל אפליקציות מערכת ואפליקציות OEM שהותקנו מראש. הכלי להגבלת הזיכרון לא תומך ברשימות היתרים כדי להחריג אפליקציות ספציפיות, וכך הוא מבטיח ניהול הוגן של הזיכרון בכל האפליקציות.
אין ממשקי API של שאילתות בזמן ריצה: ב-Android 17 ואילך, אפליקציות לא יכולות לשלוח שאילתות באופן פרוגרמטי לגבי מגבלות הזיכרון שהוקצו להן בזמן ריצה. המגבלות הרגילות של הפלטפורמה מוגדרות עם מרווחים גדולים, כך שאפליקציות שמתנהגות בצורה תקינה פועלות ללא מגבלות במהלך שימוש רגיל.
החזרת זיכרון ליבה ודינמיקת החלפה: כשמתקרבים למגבלת
memory.highשל תהליך באפליקציה, ליבת Linux מנחה את החזרת הזיכרון ספציפית לקבוצת הבקרה של התהליך הזה. הפעולות האלה כוללות השמטה של דפים לא פעילים שגובו בקובץ והחלפה של דפים אנונימיים לא פעילים ל-ZRAM, כדי לשמור על זרימה חלקה של המערכת בלי ליצור עומס על הזיכרון.
פקודות Shell
הפקודה am memory-limiter מאפשרת למפתחים ולמשלבי מערכות ליצור אינטראקציה עם השירות בזמן הריצה לצורך פיתוח, בדיקה וניפוי באגים:
am memory-limiter <SUB-COMMAND>status
בדוח של פקודת המשנה status מפורטים הסטטוס התפעולי והמדדים הפעילים של Memory Limiter:
adb shell am memory-limiter statusפלט לדוגמה:
Memory limiter
enabled monitoring=true ignored=none
visibleMem=1948MB visibleSwap=974MB
notVisibleMem=974MB notVisibleSwap=487MB
started=36 watched=36 watch-failed=0
events=0 processes=36 process-hwm=36
השדות העיקריים בפלט כוללים:
monitoring- מציין אם התהליכים נמצאים במעקב פעיל של Memory Limiter.
visibleMemוגםnotVisibleMem- מגבלות הזיכרון המוחלטות המחושבות (
memory.high) שנאכפות כרגע עבור כל מצב חשיפה. visibleSwapוגםnotVisibleSwap- המגבלות המוחלטות על החלפה (
memory.swap.max) שנאכפות כרגע עבור כל מצב חשיפה. events- מספר הפעמים שתהליך חרג מהמגבלה שהוקצתה לו.
processes- המספר הנוכחי של תהליכים שנמצאים במעקב.
ignore
פקודת המשנה ignore מחריגה באופן זמני מזיכרון מוגבל UID ספציפי או את כל התהליכים. המדד הזה שימושי להשוואת ביצועים, לבדיקת עומס או לאבחון התנהגות הזיכרון:
# Ignore a specific UID adb shell am memory-limiter ignore 10087# Ignore all processes (temporarily disables limiting) adb shell am memory-limiter ignore all# Resume normal limiting operation adb shell am memory-limiter ignore none
ידני
פקודת המשנה manual מבטלת את המגבלות המחושבות לתהליך ספציפי לפי מזהה התהליך (PID) עם ערך מוחלט מותאם אישית בבייט. הערך צריך להיות מספר שלם, אבל אפשר לכלול את הסיומת MB כדי לציין MiB או את הסיומת GB כדי לציין GiB:
# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1073741824# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1024MB# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1GB# Remove the manual override for PID 1234 adb shell am memory-limiter manual 1234 none
השינויים הידניים חלים רק למשך משך החיים של מופע התהליך הספציפי הזה. אם התהליך יופעל מחדש, הוא יחזור למגבלות הרגילות בהתאם למצב שלו.
אי אפשר לחרוג ממגבלות המערכת הפיזיות באמצעות שינוי ידני.