למד כיצד להשתמש ב-Chrome וב-DevTools כדי למצוא בעיות זיכרון המשפיעות על ביצועי הדף, כולל דליפות זיכרון, נפיחות בזיכרון ואיסוף אשפה תכוף.
סיכום
- אתם יכולים להשתמש במנהל המשימות של Chrome כדי לבדוק כמה זיכרון הדף שלכם משתמש.
- הצגת השימוש בזיכרון לאורך זמן באמצעות הקלטות של ציר הזמן.
- זיהוי של עצי DOM מנותקים (סיבה נפוצה לזיהום זיכרון) באמצעות תמונות מצב של ערימות (heap snapshot).
- בעזרת ההקלטות של ציר הזמן של ההקצאות תוכלו לדעת מתי מתבצעת הקצאה של זיכרון חדש ב-heap של JS.
- זיהוי רכיבים מנותקים שנשמרים על ידי הפניה ב-JavaScript.
סקירה כללית
בהתאם למודל הביצועים RAIL, מומלץ להתמקד במשתמשים כשאתם משפרים את הביצועים.
חשוב לבדוק בעיות שקשורות לזיכרון כי לרוב המשתמשים יכולים להבחין בהן. משתמשים יכולים להבחין בבעיות שקשורות לזיכרון בדרכים הבאות:
- הביצועים של דף מסוים הולכים ופוחתים עם הזמן. יכול להיות שמדובר בתסמין של דליפת זיכרון. דליפת זיכרון מתרחשת כשבאג בדף גורם לדף להשתמש יותר ויותר בזיכרון עם הזמן.
- ביצועי הדף גרועים באופן עקבי. יכול להיות שמדובר בתסמין של זיכרון מיותר. הצטברות זיכרון מיותר היא מצב שבו דף משתמש בזיכרון יותר ממה שנחוץ כדי להגיע למהירות דף אופטימלית.
- הביצועים של דף מסוים מושהים או שנראה שהוא מושהה לעיתים קרובות. יכול להיות שמדובר בתסמין של איסוף אשפה תדיר. איסוף אשפה הוא תהליך שבו הדפדפן משחרר זיכרון. הדפדפן הוא זה שמחליט מתי זה יקרה. במהלך האוספים, כל ביצוע הסקריפטים מושהה. לכן, אם הדפדפן מבצע הרבה איסוף אשפה, הפעלת הסקריפט תושהה הרבה.
התנפחויות בזיכרון: מהו 'יותר מדי'?
קל להגדיר דליפת זיכרון. אם האתר משתמש יותר ויותר בזיכרון, יש לכם דליפת זיכרון. אבל קשה יותר לזהות את הגורמים להצטברות זיכרון. מה נחשב ל'שימוש בזיכרון רב מדי'?
אין כאן נתונים מדויקים, כי למכשירים ולדפדפנים שונים יש יכולות שונות. אותו דף שפועל בצורה חלקה בסמארטפון מתקדם עלול לקרוס בסמארטפון פשוט.
המפתח כאן הוא להשתמש במודל RAIL ולהתמקד במשתמשים. כדאי לבדוק אילו מכשירים פופולריים בקרב המשתמשים שלכם, ואז לבדוק את הדף במכשירים האלה. אם החוויה גרועה באופן עקבי, יכול להיות שהדף חורג מיכולות הזיכרון של המכשירים האלה.
מעקב אחרי השימוש בזיכרון בזמן אמת באמצעות מנהל המשימות של Chrome
אפשר להשתמש ב'מנהל המשימות' של Chrome כנקודת התחלה לחקירה של בעיות בזיכרון. מנהל המשימות הוא מכשיר למעקב בזמן אמת שמראה כמה זיכרון נדרש לדף.
כדי לפתוח את מנהל המשימות, מקישים על Shift+Esc או עוברים לתפריט הראשי של Chrome ובוחרים באפשרות כלים נוספים > מנהל משימות.
לוחצים לחיצה ימנית על כותרת הטבלה של מנהל המשימות ומפעילים את האפשרות זיכרון JavaScript.
שתי העמודות האלה מספקות מידע שונה על אופן השימוש של הדף בזיכרון:
- העמודה זיכרון שבשימוש מייצגת את הזיכרון של מערכת ההפעלה. צומתי DOM מאוחסנים בזיכרון של מערכת ההפעלה. אם הערך הזה הולך וגדל, המשמעות היא שנוצרים צמתים ב-DOM.
העמודה JavaScript Memory מייצגת את אשכול ה-JS. העמודה הזו מכילה שני ערכים. הערך הרצוי הוא המספר הפעיל (המספר בסוגריים). המספר הפעיל מייצג את נפח הזיכרון שהאובייקטים שניתנים לגישה בדף שלכם משתמשים בו. אם המספר הזה הולך וגדל, סימן שנוצרים אובייקטים חדשים או שהאובייקטים הקיימים גדלים.
הצגה חזותית של דליפות זיכרון באמצעות הקלטות ביצועים
אפשר גם להשתמש בחלונית ביצועים כנקודת התחלה נוספת בחקירה. בעזרת החלונית 'ביצועים' אפשר לראות באופן חזותי את השימוש בזיכרון של דף לאורך זמן.
- פותחים את החלונית ביצועים ב-DevTools.
- מסמנים את התיבה זיכרון.
- ביצוע הקלטה.
כדי להדגים את ההקלטות של זיכרון ביצועים, אפשר להשתמש בקוד הבא:
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
.
הרצת הקוד הזה יוצרת הקלטה ב-Timeline כמו בצילום המסך הבא:
קודם, הסבר על ממשק המשתמש. התרשים HEAP בחלונית Overview (מתחת ל-NET) מייצג את אשכול ה-JS. מתחת לחלונית סקירה כללית מופיעה חלונית ספירה לאחור. כאן אפשר לראות פירוט של השימוש בזיכרון לפי ערימה של JavaScript (זהה לתרשים HEAP בחלונית Overview), מסמכים, צומתי DOM, מאזינים וזיכרון של GPU. השבתה של תיבת סימון מסתירה אותה מהתרשים.
עכשיו, ניתוח הקוד בהשוואה לצילום המסך. אם מעיינים בספירת הצמתים (התרשים הירוק), אפשר לראות שהיא תואמת לקוד. מספר הצמתים גדל בשלבים נפרדים. אפשר להניח שכל עלייה במספר הצמתים היא קריאה ל-grow()
. תרשים אשכול ה-JS (התרשים הכחול) לא פשוט כל כך. בהתאם לשיטות המומלצות, הירידה הראשונה היא למעשה איסוף אשפה מאולץ (שמתקבל על ידי לחיצה על הלחצן collect garbage). ככל שההקלטה מתקדמת, אפשר לראות עליות חדות בגודל הערימה של JS. זהו מצב טבעי וצפוי: קוד JavaScript יוצר את צמתים ה-DOM בכל לחיצה על הלחצן ומבצע הרבה עבודה כשיוצר את המחרוזת של מיליון התווים. הדבר החשוב כאן הוא העובדה שהערימה של JS מסתיימת ברמה גבוהה יותר מזו שבה היא התחילה ('ההתחלה' כאן היא הנקודה אחרי איסוף האשפה המאולץ). בעולם האמיתי, אם תראו את התבנית הזו של הגדלת הגודל של אשכול ה-JS או של הצומת, יכול להיות שמדובר בדליפה בזיכרון.
איתור דליפות זיכרון בעץ DOM מנותק באמצעות קובצי snapshot של ערימות
אפשר לבצע איסוף אשפה של צומת DOM רק אם אין הפניות אליו מעץ ה-DOM של הדף או מקוד ה-JavaScript. קשר נחשב 'מנותק' כשהוא מוסר מעץ ה-DOM אבל עדיין יש לו הפניות ב-JavaScript. צומתי DOM מנותקים הם גורם נפוץ לזיהום זיכרון. בקטע הזה תלמדו איך להשתמש בפרופילרים של אשכול ב-DevTools כדי לזהות צמתים מנותקים.
הנה דוגמה פשוטה לצמתים 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, ולכן הם מנותקים.
קובצי snapshot של אשכול הם אחת מהדרכים לזהות צמתים מנותקים. כפי שרואים מהשם, תמונות מצב של ערימות (heap snapshot) מציגות את אופן חלוקת הזיכרון בין אובייקטי ה-JS וצומתי ה-DOM בדף בנקודת הזמן של צילום המצב.
כדי ליצור תמונת מצב, פותחים את DevTools ועוברים לחלונית Memory, בוחרים בלחצן הבחירה Heap Snapshot ואז לוחצים על הלחצן Take snapshot.
עיבוד וטעינה של קובץ ה-snapshot עשויים להימשך זמן מה. בסיום, בוחרים את הקובץ בחלונית הימנית (שנקראת Snapshots of Heap).
מקלידים Detached
בתיבת הקלט Class filter כדי לחפש עצי DOM מנותקים.
מרחיבים את הסמלים כדי לבדוק עץ מנותק.
לוחצים על צומת כדי לבדוק אותו לעומק. בחלונית Objects תוכלו לראות מידע נוסף על הקוד שמפנה אליו. לדוגמה, בצילום המסך הבא אפשר לראות שהמשתנה detachedTree
מפנה לצומת. כדי לתקן את דליפת הזיכרון הספציפית הזו, צריך לבדוק את הקוד שמשתמש ב-detachedTree
ולוודא שהוא מסיר את ההפניה לצומת כשאין יותר צורך בה.
זיהוי דליפות זיכרון בערימה של JS באמצעות צירי זמן של הקצאות
ציר הזמן של ההקצאות הוא כלי נוסף שיכול לעזור לכם לאתר דליפות זיכרון בערימה של JS.
כדי להמחיש את ציר הזמן להקצאה, נשתמש בקוד הבא:
var x = [];
function grow() {
x.push(new Array(1000000).join('x'));
}
document.getElementById('grow').addEventListener('click', grow);
בכל פעם שמקישים על הלחצן שמצוין בקוד, מחרוזת של מיליון תווים מתווספת למערך x
.
כדי להקליט ציר זמן הקצאות, פותחים את DevTools, עוברים לחלונית זיכרון, בוחרים בלחצן הבחירה הקצאות בציר הזמן, לוחצים על הלחצן הקלטה
, מבצעים את הפעולה שסביר להניח שהיא גורמת לדליפה בזיכרון ולוחצים על הלחצן עצירת ההקלטה בסיום.בזמן הצילום, שימו לב אם מופיעים פסים כחולים בציר הזמן של ההקצאות, כמו בצילום המסך הבא.
העמודות הכחולות מייצגות הקצאות זיכרון חדשות. הקצאות הזיכרון החדשות האלה הן המועמדות לזיהוי דליפות זיכרון. אפשר להגדיל את התצוגה של העמודה כדי לסנן את החלונית Constructor כך שתוצגו רק אובייקטים שהוקצו במסגרת הזמן שצוינה.
מרחיבים את האובייקט ולוחצים על הערך שלו כדי להציג פרטים נוספים עליו בחלונית Object. לדוגמה, בצילום המסך שבהמשך, אפשר לראות בפרטים של האובייקט שהוקצה לאחרונה שהוא הוקצה למשתנה x
בהיקף Window
.
בדיקת הקצאת הזיכרון לפי פונקציה
כדי להציג את הקצאת הזיכרון לפי פונקציית JavaScript, משתמשים בסוג הפרופיל דגימת הקצאות בחלונית זיכרון.
- בוחרים בלחצן הבחירה Allocation sampling. אם יש בדף עובד, אפשר לבחור אותו כיעד ליצירת פרופיל בחלון Select JavaScript VM instance.
- לוחצים על הלחצן התחלה.
- מבצעים את הפעולות בדף שרוצים לבדוק.
- כשמסיימים את כל הפעולות, לוחצים על הלחצן עצירה.
בכלי הפיתוח מוצג פירוט של הקצאת הזיכרון לפי פונקציה. תצוגת ברירת המחדל היא כבדה (מלמטה למעלה), שבה הפונקציות שהוקצו להן הכי הרבה זיכרון מוצגות בחלק העליון.
זיהוי אובייקטים שנשמרים על ידי הפניה ב-JS
בפרופיל רכיבים מנותקים מוצגים רכיבים מנותקים שנשמרים כי יש להם הפניה בקוד JavaScript.
כדי לראות את צמת ה-HTML ואת מספר הצמתים המדויק, מתעדים פרופיל רכיבים מנותקים.
זיהוי של פעולות איסוף אשפה תכופות
אם הדף נראה מושהה לעיתים קרובות, יכול להיות שיש בעיות באיסוף אשפה.
אתם יכולים להשתמש בנתוני הזיכרון של ציר הזמן או במנהל המשימות של Chrome כדי לזהות אשכולות אשפה תכופים. ב-Task Manager, ערכים של זיכרון או זיכרון JavaScript שעולים ויורדים בתדירות גבוהה מייצגים איסוף אשפה תדיר. בהקלטות של ציר הזמן, גרפים של ערימה של JS או של מספר צמתים שעולים ויורדים בתדירות גבוהה מצביעים על איסוף אשפה תדיר.
אחרי שמזהים את הבעיה, אפשר להשתמש בהקלטה של ציר הזמן של ההקצאות כדי לבדוק איפה מתבצעת ההקצאה של הזיכרון ואילו פונקציות גורמות להקצאות.