דימון (daemon) להוצאה משימוש של פריטים בזיכרון נמוך

התהליך של הדימון (daemon) של Android low memory killer ‏(lmkd) עוקב אחרי מצב הזיכרון של מערכת Android שפועלת, ומגיב לעומס גבוה על הזיכרון על ידי סגירת התהליכים הכי פחות חיוניים כדי לשמור על רמת ביצועים מקובלת של המערכת.

מידע על עומס על הזיכרון

במערכת Android שבה פועלים כמה תהליכים במקביל, יכולים להיות מצבים שבהם הזיכרון של המערכת מלא, ותהליכים שדורשים יותר זיכרון יחוו עיכובים משמעותיים. עומס על הזיכרון, מצב שבו הזיכרון של המערכת מתמלא, מחייב את Android לפנות זיכרון (כדי להקל על העומס) על ידי ויסות נתונים (throttle) או סיום של תהליכים לא חשובים, שליחת בקשה לתהליכים לפנות משאבים לא קריטיים במטמון וכו'.

בעבר, מערכת Android ניהלה מעקב אחרי העומס על זיכרון המערכת באמצעות מנהל התקן של LMK (הפסקת תהליכים בגלל מחסור בזיכרון) בתוך ליבת המערכת. זהו מנגנון נוקשה שמסתמך על ערכים שמוגדרים בהארדקוד. החל מגרסת ליבת 4.12, מנהל ההתקן LMK הוסר מהליבה של upstream, ומרחב המשתמש lmkd מבצע משימות של מעקב אחרי הזיכרון והפסקת תהליכים.

מידע על עצירה בגלל לחץ

‫Android 10 ואילך תומכים במצב lmkd חדש שמשתמש בנתוני PSI (מידע על לחץ על הליבה) כדי לזהות לחץ על הזיכרון. ערכת התיקונים של PSI בקרנל במעלה הזרם (backported לקרנלים 4.9 ו-4.14 ) מודדת את משך הזמן שבו המשימות מתעכבות כתוצאה ממחסור בזיכרון. העיכובים האלה משפיעים ישירות על חוויית המשתמש, ולכן הם מדד נוח לקביעת רמת החומרה של העומס על הזיכרון. הקרנל במעלה הזרם כולל גם כלי מעקב אחר PSI שמאפשרים לתהליכים במרחב המשתמש עם הרשאות (כמו lmkd) לציין ערכי סף לעיכובים האלה ולהירשם לאירועים מהקרנל כשערך סף נחצה.

מעקב אחר PSI לעומת אותות vmpressure

מכיוון שאותות vmpressure (שנוצרים על ידי ליבת מערכת ההפעלה לצורך זיהוי עומס על הזיכרון ומשמשים את lmkd) כוללים לעיתים קרובות הרבה תוצאות חיוביות שגויות, lmkd צריך לבצע סינון כדי לקבוע אם הזיכרון נמצא תחת עומס אמיתי. כתוצאה מכך מתבצעות lmkd התעוררויות מיותרות ונעשה שימוש במשאבי מחשוב נוספים. השימוש ב-PSI לצורך מעקב אחר התוצאות מאפשר לזהות בצורה מדויקת יותר את העומס על הזיכרון ומצמצם את התקורה של הסינון.

שימוש במוניטורים של PSI

כדי להשתמש ב-PSI monitors במקום באירועי vmpressure, צריך להגדיר את המאפיין ro.lmk.use_psi. ברירת המחדל היא true, ולכן PSI הוא מנגנון ברירת המחדל לזיהוי עומס על הזיכרון ב-lmkd. מכיוון שנדרשת תמיכה בליבה כדי להשתמש בנתוני PSI, הליבה צריכה לכלול את תיקוני ה-backport של PSI, וצריך לקמפל אותה עם תמיכה מופעלת ב-PSI ‏ (CONFIG_PSI=y).

חסרונות של מנהל התקן LMK בליבה

מערכת Android מוציאה משימוש את מנהל ההתקן LMK בגלל כמה בעיות, כולל:

  • היה צריך לבצע אופטימיזציה אגרסיבית במכשירים עם זיכרון RAM נמוך, וגם אז הביצועים שלהם היו נמוכים בעומסי עבודה עם מטמון גדול של דפים פעילים שגובה על ידי קובץ. הביצועים הגרועים גרמו לטלטול וללא הריגות.
  • מנהל ההתקן של ליבת ה-LMK הסתמך על מגבלות של זיכרון פנוי, ללא שינוי גודל על סמך העומס על הזיכרון.
  • בגלל העיצוב הנוקשה, שותפים רבים התאימו אישית את מנהל ההתקן כדי שהוא יפעל במכשירים שלהם.
  • הדריבר של LMK התחבר ל-API של slab shrinker, שלא תוכנן לפעולות כבדות כמו חיפוש יעדים והפסקת הפעולה שלהם, ולכן הוא האט את התהליך של vmscan.

Userspace lmkd

מרחב המשתמש lmkd מיישם את אותה פונקציונליות כמו מנהל ההתקן בתוך הליבה, אבל הוא משתמש במנגנונים קיימים של הליבה כדי לזהות ולהעריך את העומס על הזיכרון. מנגנונים כאלה כוללים שימוש באירועי vmpressure שנוצרו על ידי ליבת המערכת או במוניטורים של מידע על לחץ עצירה (PSI) כדי לקבל התראות על רמות לחץ הזיכרון, ושימוש בתכונות של קבוצות בקרה של זיכרון (memory cgroup) כדי להגביל את משאבי הזיכרון שמוקצים לכל תהליך על סמך חשיבות התהליך.

שימוש ב-lmkd במרחב המשתמש ב-Android 10

ב-Android 9 ומעלה, מרחב המשתמש lmkd מופעל אם לא מזוהה דרייבר LMK בתוך הליבה. מכיוון שמרחב משתמש lmkd דורש תמיכה בליבה ב-cgroups של זיכרון, צריך לקמפל את הליבה עם הגדרות התצורה הבאות:

CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

ביטול אסטרטגיות

מרחב המשתמש lmkd תומך באסטרטגיות להפסקת פעולה שמבוססות על אירועים של vmpressure או על PSI monitors, על חומרת האירועים ועל רמזים אחרים כמו ניצול הזיכרון הווירטואלי. אסטרטגיות הסגירה שונות במכשירים עם זיכרון נמוך ובמכשירים עם ביצועים גבוהים:

  • במכשירים עם זיכרון נמוך, המערכת צריכה לעמוד בעומס זיכרון גבוה יותר כמצב פעולה רגיל.
  • במכשירים עם ביצועים גבוהים, עומס על הזיכרון צריך להיחשב כמצב לא תקין ולטפל בו לפני שהוא משפיע על הביצועים הכוללים.

אפשר להגדיר את אסטרטגיית ההפסקות באמצעות המאפיין ro.config.low_ram.

בנוסף, מרחב המשתמש lmkd תומך במצב מדור קודם שבו הוא מקבל החלטות לגבי סגירת תהליכים באמצעות אותן אסטרטגיות שבהן משתמש מנהל ההתקן LMK בתוך הליבה (כלומר, זיכרון פנוי וערכי סף של מטמון קבצים). כדי להפעיל את מצב Legacy, מגדירים את המאפיין ro.lmk.use_minfree_levels לערך true.

הגדרת lmkd

כדי להגדיר את lmkd למכשיר ספציפי, משתמשים במאפיינים הבאים.

מאפיין (property) שימוש ברירת מחדל
ro.config.low_ram מציינים אם המכשיר הוא מכשיר עם זיכרון RAM נמוך או מכשיר עם ביצועים גבוהים. false
ro.lmk.use_psi מומלץ להשתמש בכלי PSI monitors (במקום באירועי vmpressure). true
ro.lmk.use_minfree_levels שימוש בספי זיכרון פנוי ובמטמון קבצים כדי לקבל החלטות לגבי הפסקת תהליכים (כלומר, התאמה לפונקציונליות של מנהל ההתקן LMK בתוך ליבת המערכת). false
ro.lmk.low הציון המינימלי oom_adj לתהליכים שעומדים בדרישות להפסקת פעולה ברמה נמוכה של vmpressure. 1001
(מושבת)
ro.lmk.medium הציון המינימלי של oom_adj לתהליכים שעומדים בדרישות להפסקת פעולה ברמה בינונית של vmpressure. 800
(שירותים ששמורים במטמון או לא חיוניים)
ro.lmk.critical הציון המינימלי של oom_adj לתהליכים שעומדים בדרישות להפסקת פעולה ברמה קריטית vmpressure. 0
(כל תהליך)
ro.lmk.critical_upgrade הפעלת השדרוג לרמה קריטית. false
ro.lmk.upgrade_pressure הערך המקסימלי של mem_pressure שבו הרמה משודרגת כי המערכת מבצעת החלפה רבה מדי. 100
(מושבת)
ro.lmk.downgrade_pressure ערך ה-mem_pressure המינימלי שבו אירוע vmpressure מוזנח כי עדיין יש מספיק זיכרון פנוי. 100
(מושבת)
ro.lmk.kill_heaviest_task הפסקת המשימה הכבדה ביותר שעומדת בדרישות (ההחלטה הטובה ביותר) לעומת כל משימה שעומדת בדרישות (החלטה מהירה). false
ro.lmk.kill_timeout_ms משך הזמן באלפיות השנייה אחרי השבתה, שבמהלכו לא תתבצע השבתה נוספת. 0
(מושבת)
ro.lmk.debug מפעילים את lmkd יומני ניפוי הבאגים. false

הגדרה לדוגמה של מכשיר:

PRODUCT_PROPERTY_OVERRIDES += \
    ro.lmk.low=1001 \
    ro.lmk.medium=800 \
    ro.lmk.critical=0 \
    ro.lmk.critical_upgrade=false \
    ro.lmk.upgrade_pressure=100 \
    ro.lmk.downgrade_pressure=100 \
    ro.lmk.kill_heaviest_task=true

משתמשים ב-lmkd ב-Android 11

ב-Android 11 שיפרנו את lmkd באמצעות אסטרטגיה חדשה לסגירת אפליקציות. שיטת ההרג משתמשת במנגנון PSI לזיהוי לחץ זיכרון שהוצג ב-Android 10. ‫lmkd ב-Android 11 מביא בחשבון את רמות השימוש במשאבי הזיכרון ואת ה-thrashing כדי למנוע מחסור בזיכרון והידרדרות בביצועים. האסטרטגיה הזו להפסקת פעילות מחליפה אסטרטגיות קודמות, ואפשר להשתמש בה במכשירים עם ביצועים גבוהים ובמכשירים עם זיכרון RAM נמוך (Android Go).

דרישות ליבה

במכשירי Android 11, ‏ lmkd נדרשות תכונות הליבה הבאות:

  • כולל תיקוני PSI ומפעיל PSI (backports זמינים בליבות משותפות של Android בגרסאות 4.9,‏ 4.14 ו-4.19).
  • הוספת תיקונים לתמיכה ב-PIDFD (תיקונים שזמינים בגרסאות קודמות בליבות הנפוצות של Android 4.9,‏ 4.14 ו-4.19).
  • במכשירים עם זיכרון RAM נמוך, צריך לכלול cgroup של זיכרון.

צריך לקמפל את הליבה עם הגדרות התצורה הבאות:

CONFIG_PSI=y

הגדרת lmkd ב-Android 11

אסטרטגיית ניהול הזיכרון ב-Android 11 תומכת בפרמטרים ובערכי ברירת המחדל שמפורטים בהמשך. אפשר להשתמש בתכונות האלה גם במכשירים עם ביצועים גבוהים וגם במכשירים עם זיכרון RAM נמוך.

מאפיין (property) שימוש ברירת מחדל
ביצועים גבוהים זיכרון RAM נמוך
ro.lmk.psi_partial_stall_ms סף העצירה החלקי של PSI, באלפיות השנייה, להפעלת התראה על זיכרון נמוך. אם המכשיר מקבל התראות על עומס זיכרון מאוחר מדי, כדאי להקטין את הערך הזה כדי שההתראות יופעלו מוקדם יותר. אם ההתראות על עומס בזיכרון מופעלות שלא לצורך, צריך להגדיל את הערך הזה כדי שהמכשיר יהיה פחות רגיש לרעש. 70 200
ro.lmk.psi_complete_stall_ms ערך הסף המלא של PSI stall, באלפיות השנייה, להפעלת התראות קריטיות לגבי זיכרון. אם המכשיר מקבל התראות על עומס זיכרון קריטי מאוחר מדי, כדאי להקטין את הערך הזה כדי שההתראות יופעלו מוקדם יותר. אם ההתראות על עומס זיכרון קריטי מופעלות שלא לצורך, צריך להגדיל את הערך הזה כדי שהמכשיר יהיה פחות רגיש לרעשי רקע. 700
ro.lmk.thrashing_limit הכמות המקסימלית של שגיאות חוזרות ב-workingset כאחוז מהגודל הכולל של מטמון הדפים שגובה על ידי קובץ. אם הערך של Workingset refaults גבוה מהערך הזה, המשמעות היא שהמערכת מבצעת thrashing של מטמון הדפים שלה. אם העומס על הזיכרון משפיע על הביצועים של המכשיר, כדאי להקטין את הערך כדי להגביל את התופעה של החלפת דפים תכופה מדי. אם הביצועים של המכשיר מושבתים שלא לצורך בגלל פעולות שגורמות לשימוש מוגזם בזיכרון, צריך להגדיל את הערך כדי לאפשר יותר פעולות כאלה. 100 30
ro.lmk.thrashing_limit_decay הסף של thrashing decay מוצג כאחוז מהסף המקורי שמשמש להורדת הסף כשהמערכת לא משתקמת, גם אחרי השבתה. אם מתרחשת פעילות אינטנסיבית ומתמשכת של החלפת דפים שמובילה להפסקות מיותרות, צריך להקטין את הערך. אם התגובה לשימוש יתר רציף אחרי הפסקת הפעולה איטית מדי, צריך להגדיל את הערך. 10 50
ro.lmk.swap_util_max הכמות המקסימלית של זיכרון שהועבר כחלק היחסי מתוך הזיכרון הכולל שניתן להעברה. אם הזיכרון הווירטואלי גדל מעבר למגבלה הזו, זה אומר שהמערכת החליפה את רוב הזיכרון הווירטואלי שלה, והיא עדיין נמצאת בלחץ. המצב הזה יכול לקרות כשמקצים זיכרון שלא ניתן להחלפה, ונוצר עומס על הזיכרון שלא ניתן להקל עליו באמצעות החלפה כי רוב הזיכרון שניתן להחלפה כבר הוחלף. ערך ברירת המחדל הוא 100, מה שמשבית את הבדיקה הזו. אם הביצועים של המכשיר מושפעים במהלך לחץ על הזיכרון, בזמן ששימוש בהחלפה גבוה ורמת ההחלפה הפנויה לא יורדת ל-ro.lmk.swap_free_low_percentage, צריך להקטין את הערך כדי להגביל את השימוש בהחלפה. 100 100

בנוסף, ידיות הכוונון הישנות הבאות פועלות גם עם אסטרטגיית ההשבתה החדשה.

מאפיין (property) שימוש ברירת מחדל
ביצועים גבוהים זיכרון RAM נמוך
ro.lmk.swap_free_low_percentage רמת ההחלפה החופשית כאחוז מסך כל שטח ההחלפה. ‫`lmkd` משתמש בערך הזה כסף לזיהוי מצב שבו אין מספיק זיכרון וירטואלי במערכת. אם התהליך `lmkd` מסתיים בגלל שיש יותר מדי מקום בהחלפה, צריך להקטין את האחוז. אם ההפסקות של `lmkd` מתרחשות מאוחר מדי, מה שמאפשר הפסקות של OOM, צריך להגדיל את אחוז הזיכרון. 20 10
ro.lmk.debug ההגדרה הזו מפעילה יומני ניפוי באגים של ‎ `lmkd` ‎. מפעילים את ניפוי הבאגים במהלך ההתאמה. false