מעקב אחר השימוש הכולל בזיכרון בדף האינטרנט שלך באמצעותmeasureUserAgentspecificMemory()

איך למדוד את השימוש בזיכרון של דף האינטרנט בסביבת הייצור כדי לזהות רגרסיות.

Brendan Kenny
Brendan Kenny
Ulan Degenbaev
Ulan Degenbaev

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

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

const object = {a: new Array(1000), b: new Array(2000)};
setInterval(() => console.log(object.a), 1000);

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

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

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

מה ההבדל בין performance.measureUserAgentSpecificMemory() לבין ה-API הקודם של performance.memory?

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

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

הצעות לתרחישים לדוגמה

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

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

תאימות דפדפן

תמיכה בדפדפן

  • Chrome: 89.
  • קצה: 89.
  • Firefox: לא נתמך.
  • Safari: לא נתמך.

מקור

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

שימוש ב-performance.measureUserAgentSpecificMemory()

זיהוי תכונות

הפונקציה performance.measureUserAgentSpecificMemory לא תהיה זמינה או שהיא עשויה תיכשל עם SecurityError אם סביבת הביצוע לא מתאימה דרישות האבטחה למניעת דליפות מידע בין מקורות. השיטה הזו מסתמכת על בידוד בין מקורות, ושדף אינטרנט יכול להפעיל. באמצעות הגדרה של כותרות COOP+COEP.

אפשר לזהות תמיכה בזמן הריצה:

if (!window.crossOriginIsolated) {
  console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
} else if (!performance.measureUserAgentSpecificMemory) {
  console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
} else {
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
    } else {
      throw error;
    }
  }
  console.log(result);
}

בדיקות מקומיות

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

קריאה ל-API מאלצת איסוף אשפה לאחר פרק זמן מסוים, ולכן מוגדר כרגע ל-20 שניות, אבל יכול להיות שזה יקרה מוקדם יותר. הפעלת Chrome עם סימון שורת הפקודה --enable-blink-features='ForceEagerMeasureMemory' מצטמצם את הזמן הקצוב לתפוגה להיות אפס, והוא שימושי לניפוי באגים ולבדיקה מקומיים.

דוגמה

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

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

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

function scheduleMeasurement() {
  // Check measurement API is available.
  if (!window.crossOriginIsolated) {
    console.log('performance.measureUserAgentSpecificMemory() is only available in cross-origin-isolated pages');
    console.log('See https://web.dev/coop-coep/ to learn more')
    return;
  }
  if (!performance.measureUserAgentSpecificMemory) {
    console.log('performance.measureUserAgentSpecificMemory() is not available in this browser');
    return;
  }
  const interval = measurementInterval();
  console.log(`Running next memory measurement in ${Math.round(interval / 1000)} seconds`);
  setTimeout(performMeasurement, interval);
}

הפונקציה measurementInterval() מחשבת מרווח אקראי באלפיות שנייה כך שבממוצע מתבצעת מדידה אחת כל חמש דקות. ראו מעריכי של הפונקציה אם אתם מעוניינים במתמטיקה שמאחורי הפונקציה.

function measurementInterval() {
  const MEAN_INTERVAL_IN_MS = 5 * 60 * 1000;
  return -Math.log(Math.random()) * MEAN_INTERVAL_IN_MS;
}

לבסוף, הפונקציה performMeasurement() האסינכרונית מפעילה את ה-API, רשומות את התוצאה, ומתזמנת את המדידה הבאה.

async function performMeasurement() {
  // 1. Invoke performance.measureUserAgentSpecificMemory().
  let result;
  try {
    result = await performance.measureUserAgentSpecificMemory();
  } catch (error) {
    if (error instanceof DOMException && error.name === 'SecurityError') {
      console.log('The context is not secure.');
      return;
    }
    // Rethrow other errors.
    throw error;
  }
  // 2. Record the result.
  console.log('Memory usage:', result);
  // 3. Schedule the next measurement.
  scheduleMeasurement();
}

לבסוף, מתחילים למדוד.

// Start measurements.
scheduleMeasurement();

התוצאה עשויה להיראות כך:

// Console output:
{
  bytes: 60_100_000,
  breakdown: [
    {
      bytes: 40_000_000,
      attribution: [{
        url: 'https://example.com/',
        scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 20_000_000,
      attribution: [{
          url: 'https://example.com/iframe',
          container: {
            id: 'iframe-id-attribute',
            src: '/iframe',
          },
          scope: 'Window',
      }],
      types: ['JavaScript']
    },

    {
      bytes: 100_000,
      attribution: [],
      types: ['DOM']
    },
  ],
}

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

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

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

משוב

קבוצת הקהילה של ביצועי האינטרנט וצוות Chrome ישמחו מאוד לשמוע על המחשבות והחוויות שלכם עם performance.measureUserAgentSpecificMemory().

מתארים את עיצוב ה-API

האם יש משהו ב-API שלא פועל כצפוי? או שיש מאפיינים חסרים שאתם צריכים ליישם את הרעיון שלכם? דיווח על בעיה במפרט של או מוסיפים את המאגר performance.measureUserAgentspecificMemory() ל-GitHub. להעלות את דעתכם לגבי בעיה קיימת.

דיווח על בעיה בהטמעה

מצאת באג בהטמעה של Chrome? או שההטמעה שונה מהמפרט? דווחו על באג בכתובת new.crbug.com. חשוב כדאי לכלול כמה שיותר פרטים, ולספק הוראות פשוטות לשחזור את הבאג ולהגדיר את הרכיבים כ-Blink>PerformanceAPIs. Glitch היא אפשרות טובה לשיתוף תגובות מהירות וקלות.

פנייה לתמיכה

האם בכוונתך להשתמש ב-performance.measureUserAgentSpecificMemory()? התמיכה הציבורית שלך עוזר לצוות Chrome לתעדף תכונות ומראה לספקי דפדפנים אחרים איך חיוני לתמוך בהם. שליחת ציוץ אל @ChromiumDev וספר לנו איפה אתה משתמש בו ובאיזה אופן.

קישורים שימושיים

אישורים

תודה רבה ל-Domnic Denicola, Yoav Weiss, Mathias Bynens על ביקורות על עיצוב ה-API, ו-Dominik Inführ, Hannes Payer, Kantaro Hara, Michael Lippautz – בדיקות קוד ב-Chrome. אני מודה גם לפי פארקר, פיליפ וייס, אולגה בלומסטיח', מתיו בולוהאן וניל מקיי על מתן משוב רב-ערך ממשתמשים שיפרנו את ה-API.

תמונה ראשית (Hero) מאת האריסון ברודבנט בתוכנית Unused