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 באמצעות קובץ התצורה הרגיל של הפלטפורמה במחיצת המערכת במיקום /system/etc/memory-limiter-config.xml.
פורמט XML
קובץ התצורה פועל לפי הסכימה שמוגדרת ב-memory-limiter-config.xsd. הקובץ מגדיר כמה קבוצות של מגבלות, שמסודרות לפי הזיכרון הזמין. השירות בוחר את קבוצת המגבלות התואמת הגבוהה ביותר על סמך זיכרון ה-RAM הזמין במכשיר. כל ערכי הזיכרון מצוינים במביבייט (MiB).
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM boundaries account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
<maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
<maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
<maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
<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 לרכיבי חומרה מיוחדים כמו GPU, מודם פס בסיס, ISP של מצלמה, סביבות מחשוב אמינות והיפר-ויזורים. בדרך כלל הן צורכות בין 500 MiB ליותר מ-2 GiB של זיכרון פיזי.מכיוון שהערך של
MemTotalנמוך מהערך של ה-DRAM הפיזי שמפורסם במפרט המכשיר, הסף שלminimumRequiredMemTotalכולל מרווח לערכים האלה. לדוגמה, אם מגדירים טירגוט למכשירים עם נפח אחסון של 16GB, ערך הסף יהיה14336(14GB) ולא16384(16GB). אם מגדירים טירגוט למכשירים עם נפח אחסון של 12GB, ערך הסף יהיה9216(9GB). כך אפשר לוודא שכל מכשיר תואם לרמה המיועדת שלו, בלי קשר לשינויים ברמת הלוח.maximumAllowedMemTotalהמערכת המקסימלית
MemTotal(ב-MiB, לא כולל תקרה) שחלה על קבוצת המגבלות הזו. אם ערך המדדMemTotalגדול מהערך הזה או שווה לו, המגבלה שהוגדרה לא חלה. במכשירים עם 18,432 MiB ומעלה או במכשירים עם פחות מ-3,200 MiB, המגביל לא חל ולא מגביל את התהליכים.memVisibleמגבלת הזיכרון הרכה (
memory.high) ב-MiB שחלה על תהליכים גלויים.memNotVisibleמגבלת הזיכרון הרכה (
memory.high) ב-MiB חלה על תהליכים שלא נראים.swapVisibleמגבלת מרחב ההחלפה (
memory.swap.max) ב-MiB שחלה על תהליכים גלויים.swapNotVisibleמגבלת אזור ההחלפה (
memory.swap.max) ב-MiB שחלה על תהליכים לא גלויים.
מגבלות סטנדרטיות של הפלטפורמה
ב-Android 17 ואילך, הפלטפורמה מציינת מגבלות סטנדרטיות של זיכרון ושל קובץ החלפה ב-/system/etc/memory-limiter-config.xml, על פני רמות נפוצות של זיכרון RAM פיזי, באמצעות מרווחי טווח של [floor, ceiling):
| זיכרון RAM פיזי לטירגוט |
טווח MemTotal [רצפה, תקרה) (minimumRequiredMemTotal, maximumAllowedMemTotal) |
מגבלת זיכרון גלויה (memVisible) |
מגבלת זיכרון לא גלויה (memNotVisible) |
מגבלת החלפה גלויה (swapVisible) |
מגבלת החלפה לא גלויה (swapNotVisible) |
|---|---|---|---|---|---|
| 16 GB | [14336, 18432) MiB |
10,240 MiB (10 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) | 5,120 MiB (5 GiB) |
| 12 GB | [9216, 14336) MiB |
8,192 MiB (8 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) | 4,096 MiB (4 GiB) |
| 8 GB | [6800, 9216) MiB |
5,120 MiB (5 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) | 3,072 MiB (3 GiB) |
| 6 GB | [4800, 6800) MiB |
4,096 MiB (4 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) | 2,048 MiB (2 GiB) |
| 4 GB | [3200, 4800) MiB |
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 שהותקנו מראש. הכלי Memory Limiter לא תומך ברשימות היתרים כדי להחריג אפליקציות ספציפיות, וכך הוא מבטיח ניהול הוגן של הזיכרון בכל האפליקציות.
אין ממשקי 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
השינויים הידניים חלים רק למשך חייו של מופע התהליך הספציפי הזה. אם התהליך יופעל מחדש, הוא יחזור למגבלות הרגילות בהתאם למצב שלו.
אי אפשר לחרוג ממגבלות המערכת הפיזיות באמצעות שינוי ידני.