סקירה כללית של ניהול הזיכרון

השימוש במכונה הווירטואלית של Android (ART) וב-Dalvik חלוקה לדפים ומיפוי זיכרון (רנדומיזציה) כדי לנהל את הזיכרון. כלומר, כל זיכרון של אפליקציה משנה - האם על ידי הקצאה אובייקטים חדשים או דפים ממופים באמצעות מגע - נשאר ב-RAM לא ניתן לפריסת דפים. הדרך היחידה לשחרר את הזיכרון מאפליקציה היא לשחרר לאובייקטים שמאוחסנים באפליקציה, וכך הזיכרון יהיה זמין אוסף אשפה. לעומת זאת, יש חריג אחד: כל הקבצים כלום ללא שינויים, כמו קוד, עשוי להיות מוצג מחוץ ל-RAM אם המערכת רוצה להשתמש בזיכרון הזה במקום אחר.

בדף הזה מוסבר איך מערכת Android מנהלת תהליכים של אפליקציות וזיכרון מידע נוסף על ניהול יעיל יותר של הזיכרון באפליקציה שלכם: ניהול הזיכרון של האפליקציה

איסוף אשפה

סביבת זיכרון מנוהלת, כמו המכונה הווירטואלית ART או Dalvik, עוקב אחרי כל הקצאת זיכרון. לאחר שנקבע שפיסת זיכרון כבר לא משתמשת בה, הוא מחזיר אותו לערימה ללא כל התערבות של המתכנת. המנגנון לניצול מחדש של זיכרון שאינו בשימוש בסביבת זיכרון מנוהלת נקראת איסוף אשפה. לאיסוף אשפה יש שני יעדים: למצוא אובייקטים של נתונים בתוכנית שלא ניתן לגשת אליה בעתיד; וגם לתבוע חזרה את המשאבים ששימשו את האובייקטים האלה.

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

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

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

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

למידע כללי נוסף על איסוף אשפה, אפשר לעיין במאמר איסוף אשפה.

שיתוף הזיכרון

כדי להכניס כל מה שצריך ב-RAM, מערכת Android מנסה לשתף דפי RAM בין תהליכים. הוא יכול לעשות זאת בדרכים הבאות:

  • כל תהליך אפליקציה מפוצל מתהליך קיים שנקרא Zygote. תהליך Zygote מתחיל כשהמערכת אתחול וטוענת משאבים וקוד framework (כמו נושאי פעילות). כדי להתחיל תהליך חדש לאפליקציה: המערכת מחברת את תהליך זיגוט טוען ומריץ את קוד האפליקציה בתהליך החדש. גישה זו מאפשרת לרוב דפי ה-RAM שהוקצו את ה-framework ואת המשאבים לשיתוף בכל תהליכי האפליקציה.
  • רוב הנתונים הסטטיים נדחפים לתהליך. השיטה הזו מאפשרת לשתף נתונים בין תהליכים, והוא גם מאפשר ליצור דפים כשיש צורך. נתונים סטטיים לדוגמה: קוד Dalvik (על ידי הצבתו בתוך .odex מקושר מראש קובץ ל-mapping ישיר), משאבי אפליקציות (על ידי תכנון טבלת המשאבים כך שתהיה מבנה שאפשר לדחוס אותם ועל ידי יישור הרוכסן של ה-APK), כמו קוד מקורי בקובצי .so.
  • במקומות רבים, אותה דינמיקה ב-Android RAM בתהליכים שונים באמצעות הקצאה מפורשת אזורי זיכרון משותפים (עם ashmem או gralloc). לדוגמה, פלטפורמות החלונות כוללות את הזיכרון בין האפליקציה למרכיב המסך, וגם בתהליך האחסון הזמני של הסמן משתמשים בזיכרון המשותף בין וספק תוכן.

בגלל השימוש הנרחב בזיכרון המשותף, אנחנו כמה זיכרון האפליקציה שלך משתמשת אכפתיות. שיטות לזיהוי נכון של האפליקציה נדון בשימוש בזיכרון בדיקת השימוש ב-RAM.

הקצאה וניצול מחדש של זיכרון האפליקציה

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

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

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

הגבלת זיכרון האפליקציה

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

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

מעבר בין אפליקציות

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

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

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

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

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

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

בדיקת אפליקציה מטרידה

בדיקת אפליקציה מאתגרת (stressapptest) היא בדיקה של ממשק זיכרון שעוזרת ליצור מצבים ריאליסטיים עם עומס גבוה כדי לבדוק סוגים שונים של זיכרון ומגבלות חומרה עבור האפליקציה שלך. בזכות היכולת להגדיר מגבלות זמן וזיכרון, מאפשרת לכתוב אינסטרומנטציה כדי לאמת מפגשים בעולם האמיתי במצבים של זיכרון גבוה. לדוגמה, השתמשו בקבוצת הפקודות הבאה כדי לדחוף את הספרייה הסטטית במערכת קובצי הנתונים שלכם, להפוך אותו לקובץ הפעלה ולהריץ בדיקת לחץ במשך 20 שניות מתוך 990MB:
    adb push stressapptest /data/local/tmp/
    adb shell chmod 777 /data/local/tmp/stressapptest
    adb shell /data/local/tmp/stressapptest -s 20 -M 990

  

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

תצפיות על מדדי לחץ

ניתן להשתמש בכלים כמו stressapptest כדי לבקש הקצאות זיכרון שגדולות יותר מבאופן חופשי זמינים. בקשה מהסוג הזה יכולה לשלוח התראות שונות, ושעליכם להיות מודעים להן צד הפיתוח. שלוש התראות עיקריות שאפשר להעלות בגלל זמינות נמוכה של הזיכרון:
  • SIGABRT: זוהי קריסה חמורה, מובנית לתהליך שלך עקב בקשה להקצאות של גדול יותר מהזיכרון החופשי, בזמן שהמערכת כבר נמצאת בלחץ על הזיכרון.
  • SIGQUIT: מפיק תמונת מצב של הזיכרון ומסיים את התהליך כשמזוהים על ידי בדיקת האינסטרומנטציה.
  • TRIM_MEMORY_EVENTS: הקריאות החוזרות האלה זמינות ב-Android 4.1 (רמת API 16) ואילך, ומספקות פרטים התראות זיכרון לגבי התהליך שלכם.