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

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

סיכום

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

סקירה כללית

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

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

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

צבירת זיכרון: כמה זה "יותר מדי"?

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. פותחים את החלונית ביצועים בכלי פיתוח.
  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);

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

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

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

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

גילוי דליפות זיכרון של עץ DOM מנותקות באמצעות תמונות מצב של הזיכרון

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

דוגמה פשוטה לצומתי 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) הן אחת הדרכים לזהות צמתים מנותקים. כפי שמשתמע מהשם, תמונות מצב של הזיכרון (heap snapshots) מראות האופן שבו הזיכרון מפוזר בין אובייקטי JS של הדף וצומתי DOM בנקודת הזמן של קובץ snapshot.

כדי ליצור תמונת מצב, פותחים את כלי הפיתוח ועוברים לחלונית הזיכרון, בוחרים בערימה לחצן הבחירה 'תמונת מצב', ולאחר מכן לוחצים על הלחצן צילום תמונת מצב.

צילום תמונת מצב של הזיכרון

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

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

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

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

חקירה של עץ נותק

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

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

חקירה של צומת צהוב

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

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

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

var x = [];

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

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

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

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

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

הקצאות חדשות

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

ציר הזמן להקצאה עם מרחק תצוגה

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

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

חקירת הקצאת הזיכרון לפי פונקציה

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

הקלטת פרופיל ההקצאה

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

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

פרופיל הקצאה

איתור אוספים תכופים של אשפה

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

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

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