פתרון בעיות בזיכרון

קייס בסקית
קייס בסקית

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

סיכום

  • לבדוק בכמה זיכרון הדף שלכם משתמש כרגע באמצעות מנהל המשימות של Chrome.
  • המחשה חזותית של השימוש בזיכרון לאורך זמן באמצעות הקלטות של ציר הזמן.
  • בעזרת תמונות מצב של ערימה (heap snapshots), תוכלו לזהות עצי DOM מנותקים (גורם נפוץ לדליפות זיכרון).
  • אפשר לבדוק מתי מוקצה זיכרון חדש בערימה של ה-JS באמצעות הקלטות ציר הזמן של ההקצאה.

סקירה כללית

ברוח מודל הביצועים של RAIL, מאמצי הביצועים צריכים להתמקד במשתמשים שלכם.

חשוב לזכור שלרוב יש בעיות בזיכרון, כי הרבה פעמים המשתמשים יכולים להבחין בהן. משתמשים יכולים לזהות בעיות בזיכרון בדרכים הבאות:

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

נפח זיכרון גדול מדי: כמה זה יותר מדי?

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

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

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

מעקב אחר השימוש בזיכרון בזמן אמת באמצעות מנהל המשימות של Chrome

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

  1. מקישים על Shift+Esc או עוברים לתפריט הראשי של Chrome ובוחרים באפשרות כלים נוספים > מנהל המשימות כדי לפתוח את מנהל המשימות.

    פתיחת מנהל המשימות

  2. לוחצים לחיצה ימנית על כותרת הטבלה של מנהל המשימות ומפעילים זיכרון JavaScript.

    הפעלת זיכרון JS

בשתי העמודות האלה מוסבר איך הדף משתמש בזיכרון:

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

המחשה חזותית של דליפות זיכרון באמצעות הקלטות ביצועים

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

  1. פותחים את החלונית Performance (ביצועים) בכלי הפיתוח.
  2. מסמנים את התיבה זיכרון.
  3. הקלטה.

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

var x = [];

function grow() {
  for (var i = 0; i < 10000; i++) {
    document.body.appendChild(document.createElement('div'));
  }
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

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

דוגמה לצמיחה פשוטה

ראשית, הסבר על ממשק המשתמש. התרשים HEAP בחלונית Overview (מתחת ל-NET) מייצג את הערימה של JS. מתחת לחלונית Overview נמצאת החלונית Counter. כאן תוכלו לראות את השימוש בזיכרון בחלוקה לפי ערימה של JS (כמו בתרשים HEAP בחלונית Overview), מסמכים, צומתי DOM, מאזינים וזיכרון GPU. השבתה של תיבת סימון מסתירה אותה מהתרשים.

עכשיו נערוך השוואה של הקוד עם צילום המסך. אם תסתכלו במונה הצמתים (התרשים הירוק), תוכלו לראות שהוא תואם בדיוק לקוד. מספר הצמתים גדל בשלבים השונים. ניתן להניח שכל עלייה בספירת הצמתים היא קריאה ל-grow(). תרשים הערימה של ה-JS (התרשים הכחול) לא פשוט כל כך. בהתאם לשיטות המומלצות, הטבילה הראשונה היא למעשה איסוף אשפה מאולץ (המתקבל בלחיצה על הלחצן collect garbage). עם התקדמות ההקלטה, תוכלו לראות שגודל הערימה של ה-JS עולה. זה טבעי וצפוי: קוד JavaScript יוצר את צומתי ה-DOM בכל לחיצה על לחצן, והוא מבצע עבודה רבה כשהוא יוצר את המחרוזת של מיליון תווים. הדבר המרכזי במקרה הזה הוא העובדה שהערימה (heap) של ה-JS מסתיימת גבוה יותר מכפי שהתחילה ('ההתחלה' כאן היא הנקודה שאחרי איסוף האשפה הכפויה). בעולם האמיתי, אם ראיתם את הדפוס הזה של הגדלת גודל הערימה או הצומת של JS, יכול להיות שהדבר יוביל לדליפת זיכרון.

דיווח על דליפות זיכרון של עץ DOM שהתנתקנו באמצעות תמונת מצב של הזיכרון (heap snapshots)

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

דוגמה פשוטה לצומתי DOM מנותקים.

var detachedTree;

function create() {
  var ul = document.createElement('ul');
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li');
    ul.appendChild(li);
  }
  detachedTree = ul;
}

document.getElementById('create').addEventListener('click', create);

לחיצה על הלחצן שיש אליו הפניה בקוד יוצרת צומת ul עם עשרה צאצאים של li. הקוד מפנה לצמתים האלה, אבל הם לא קיימים בעץ ה-DOM, ולכן הם מנותקים.

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

כדי ליצור תמונת מצב, פותחים את כלי הפיתוח ועוברים לחלונית Memory, לוחצים על לחצן הבחירה Heap Snapshot ואז על הלחצן Take snapshot.

צילום תמונת מצב של הזיכרון (heap snapshot)

העיבוד והטעינה של תמונת המצב עשויים להימשך זמן מה. בסיום, בוחרים אותו מהחלונית השמאלית (בשם HEAP SNAPSHOTS).

מקלידים Detached בתיבת הטקסט מסנן כיתה כדי לחפש עצי DOM מנותקים.

סינון של צמתים מנותקים

מרחיבים את קבוצת הקרטים כדי לחקור עץ מנותק.

חוקרים עץ מנותק

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

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

חיפוש של צומת צהוב

זיהוי דליפות זיכרון ערימה של JS באמצעות צירי זמן להקצאה

ציר הזמן להקצאה הוא כלי נוסף שיכול לעזור לך לאתר דליפות זיכרון בערימת ה-JS.

כדי להדגים את ציר הזמן להקצאה, מומלץ להשתמש בקוד הבא:

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

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

כדי להקליט ציר זמן להקצאה, פותחים את כלי הפיתוח, עוברים לחלונית Profiles, לוחצים על לחצן הבחירה Record Allocation Schedule, לוחצים על Start, מבצעים את הפעולה שלדעתכם גורמת לדליפת הזיכרון (לחצן להפסקת ההקלטה) ומסיימים ללחוץ על הלחצן stop record (לחצן להפסקת ההקלטה).

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

הקצאות חדשות

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

ציר זמן להקצאה, מוגדל

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

פרטי האובייקט

בדיקה של הקצאת הזיכרון לפי פונקציה

אפשר להשתמש בסוג דגימת הקצאה בחלונית זיכרון כדי להציג את הקצאת הזיכרון לפי פונקציית JavaScript.

הכלי לניתוח הקצאה להקלטה

  1. לוחצים על לחצן הבחירה דגימת הקצאה. אם יש עובד בדף, תוכלו לבחור אותו כיעד ליצירת פרופילים באמצעות התפריט הנפתח שליד הלחצן Start (התחלה).
  2. לוחצים על הלחצן התחלה.
  3. מבצעים את הפעולות בדף שרוצים לבדוק.
  4. אחרי שמסיימים את כל הפעולות, לוחצים על הלחצן עצירה.

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

פרופיל הקצאה

איתור פינוי אשפה לעיתים קרובות

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

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

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