דימון (daemon) חסר זיכרון

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

מידע על לחץ על הזיכרון

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

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

מידע על עמדת הלחץ

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

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

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

שימוש במוני PSI

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

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

מערכת Android מפסיקה את השימוש ב-LMK driver בגלל מספר בעיות, כולל:

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

lmkd במרחב המשתמש

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

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

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

CONFIG_ANDROID_LOW_MEMORY_KILLER=n
CONFIG_MEMCG=y
CONFIG_MEMCG_SWAP=y

אסטרטגיות להפסקת פעילות

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

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

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

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

הגדרת lmkd

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

נכס שימוש ברירת מחדל
ro.config.low_ram מציינים אם המכשיר כולל נפח זיכרון RAM נמוך או גבוה. false
ro.lmk.use_psi שימוש במעקב PSI (במקום אירועי 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 (יציאות לאחור זמינות בליבות נפוצות של Android בגרסאות 4.9, 4.14 ו-4.19).
  • כולל תיקוני תמיכה ב-PIDFD (גרסאות backport זמינות בליבות הנפוצות של Android בגרסאות 4.9,‏ 4.14 ו-4.19).
  • במכשירים עם זיכרון RAM נמוך, צריך לכלול קבוצות cgroup של זיכרון.

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

CONFIG_PSI=y

הגדרת lmkd ב-Android 11

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

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

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

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