ตรวจสอบการใช้หน่วยความจำทั้งหมดของหน้าเว็บด้วยmeasureUserAgentSpecificMemory()

ดูวิธีวัดการใช้หน่วยความจำของหน้าเว็บในเวอร์ชันที่ใช้งานจริงเพื่อตรวจหาการถดถอย

Brendan Kenny
Brendan Kenny
Ulan Degenbaev
Ulan Degenbaev

เบราว์เซอร์จะจัดการหน่วยความจำของหน้าเว็บโดยอัตโนมัติ เมื่อใดก็ตามที่หน้าเว็บ สร้างออบเจ็กต์ เบราว์เซอร์จะจัดสรรหน่วยความจำเป็นส่วนๆ "ภายใน" ถึง จัดเก็บออบเจ็กต์ เนื่องจากหน่วยความจำเป็นทรัพยากรที่มีจำกัด เบราว์เซอร์จึง ไฟล์ขยะเพื่อตรวจจับเมื่อไม่จำเป็นต้องใช้ออบเจ็กต์อีกต่อไปและเพื่อเพิ่มพื้นที่ว่าง ส่วนความทรงจำที่อยู่เบื้องหลัง

แต่การตรวจจับก็อาจไม่สมบูรณ์แบบ ได้รับการพิสูจน์ว่าการตรวจจับที่สมบูรณ์แบบ เป็นไปไม่ได้ ดังนั้น เบราว์เซอร์จะประมาณแนวคิดว่า "ออบเจ็กต์ จำเป็น" ว่า "เข้าถึงวัตถุได้" หากหน้าเว็บไม่สามารถ เข้าถึงออบเจ็กต์ผ่านตัวแปรและฟิลด์ของออบเจ็กต์ที่เข้าถึงได้อื่นๆ เบราว์เซอร์จะสามารถเรียกคืนอ็อบเจกต์นี้ได้อย่างปลอดภัย ความแตกต่างระหว่าง แนวความคิด 2 ส่วนทำให้เกิดการรั่วไหลของหน่วยความจำดังที่แสดงในตัวอย่างต่อไปนี้

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

ในตัวอย่างนี้ ไม่จำเป็นต้องใช้อาร์เรย์ b อีกต่อไป แต่เบราว์เซอร์ไม่ โปรดอ้างสิทธิ์อีกครั้งเนื่องจากยังคงติดต่อได้ผ่าน object.b ใน Callback ดังนั้น หน่วยความจำของอาร์เรย์ที่ใหญ่กว่าจึงรั่วไหล

การรั่วไหลของหน่วยความจำเกิดขึ้นในเว็บ แนะนำได้ง่ายๆ ด้วยการลืมยกเลิกการลงทะเบียน Listener กิจกรรม โดย จับภาพอ็อบเจกต์จาก iframe โดยไม่ได้ตั้งใจโดยไม่ปิดผู้ปฏิบัติงานด้วยการ การสะสมออบเจ็กต์ในอาร์เรย์ และอื่นๆ หากหน้าเว็บมีหน่วยความจำรั่วไหล การใช้หน่วยความจำก็มากขึ้น เมื่อเวลาผ่านไป หน้าเว็บจึงดูช้า มากเกินไปต่อผู้ใช้

ขั้นตอนแรกในการแก้ไขปัญหานี้คือการวัดค่า ฟิลด์ performance.measureUserAgentSpecificMemory() API ช่วยให้นักพัฒนาซอฟต์แวร์ดำเนินการต่อไปนี้ได้ วัดการใช้หน่วยความจำของหน้าเว็บในเวอร์ชันที่ใช้งานจริง จึงตรวจพบหน่วยความจำด้วย รั่วไหลผ่านการทดสอบในพื้นที่

performance.measureUserAgentSpecificMemory() แตกต่างจาก performance.memory API เดิมอย่างไร

หากคุณคุ้นเคยกับ performance.memory API ที่ไม่เป็นไปตามมาตรฐานที่มีอยู่แล้ว คุณอาจสงสัยว่า API ใหม่นี้แตกต่างจาก API นี้อย่างไร ความแตกต่างที่สำคัญคือ ว่า API เดิมแสดงผลขนาดของฮีป JavaScript ในขณะที่ API ใหม่ ประมาณหน่วยความจำที่หน้าเว็บใช้ ความแตกต่างนี้ สำคัญเมื่อ Chrome แชร์ฮีปเดียวกันกับหน้าเว็บหลายหน้า (หรือ หน้าเว็บเดียวกันซ้ำกันหลายครั้ง) ในกรณีดังกล่าว ผลลัพธ์ของโฆษณา API อาจปิดอยู่โดยไม่มีกฎเกณฑ์ เนื่องจาก API เดิมมีการกำหนดไว้ใน คำเฉพาะด้านการติดตั้งใช้งาน เช่น "heap" ทำให้เป็นมาตรฐานสิ้นหวัง

ข้อแตกต่างอีกประการหนึ่งคือ API ใหม่จะทำการวัดหน่วยความจำในระหว่าง กับระบบเก็บขยะ วิธีนี้ช่วยลดเสียงรบกวนในผลลัพธ์ แต่อาจใช้เวลา ไปจนกว่าจะได้ผลลัพธ์ โปรดทราบว่าเบราว์เซอร์อื่นๆ อาจเลือกที่จะ ใช้ API ใหม่ได้โดยไม่ต้องใช้กระบวนการจัดการขยะ

Use Case ที่แนะนำ

การใช้งานหน่วยความจำของหน้าเว็บจะขึ้นอยู่กับระยะเวลาของเหตุการณ์ การกระทำของผู้ใช้ และ คอลเล็กชันขยะ นี่คือเหตุผลที่ทำให้ API การวัดหน่วยความจำมีไว้เพื่อ การรวมข้อมูลการใช้หน่วยความจำจากเวอร์ชันที่ใช้งานจริง ผลลัพธ์ของการโทรแต่ละครั้ง ไม่ค่อยมีประโยชน์ ตัวอย่างกรณีการใช้งาน

  • การตรวจจับการถดถอยระหว่างการเปิดตัวหน้าเว็บเวอร์ชันใหม่เพื่อตรวจจับการรั่วไหลของหน่วยความจำใหม่
  • การทดสอบ A/B ในฟีเจอร์ใหม่เพื่อประเมินผลกระทบของหน่วยความจำและตรวจจับการรั่วไหลของหน่วยความจำ
  • เชื่อมโยงการใช้งานหน่วยความจำกับระยะเวลาเซสชันเพื่อยืนยันว่ามีหรือไม่มีหน่วยความจำรั่วไหล
  • เชื่อมโยงการใช้งานหน่วยความจำกับเมตริกผู้ใช้เพื่อทำความเข้าใจผลกระทบโดยรวมของการใช้หน่วยความจำ

ความเข้ากันได้กับเบราว์เซอร์

การรองรับเบราว์เซอร์

  • Chrome: 89.
  • ขอบ: 89
  • Firefox: ไม่สนับสนุน
  • Safari: ไม่รองรับ

แหล่งที่มา

ขณะนี้ API ได้รับการรองรับเฉพาะในเบราว์เซอร์แบบ Chromium โดยเริ่มตั้งแต่ Chrome 89 เป็นต้นไป ของ 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' ลดแฟล็กบรรทัดคำสั่ง ระยะหมดเวลาเป็น 0 และมีประโยชน์สำหรับการแก้ไขข้อบกพร่องและการทดสอบในเครื่อง

ตัวอย่าง

การใช้งานที่แนะนำของ API คือการกำหนดจอภาพหน่วยความจำทั่วโลกที่ สุ่มตัวอย่างการใช้หน่วยความจำของหน้าเว็บทั้งหน้าและส่งผลลัพธ์ไปยังเซิร์ฟเวอร์ ในการรวบรวมและการวิเคราะห์ วิธีที่ง่ายที่สุดคือสุ่มตัวอย่างเป็นระยะๆ เช่น ทุก M นาที แต่วิธีนี้ก่อให้เกิดความเอนเอียงให้กับข้อมูลเนื่องจาก อาจมีการใช้หน่วยความจำสูงสุดระหว่างกลุ่มตัวอย่าง

ตัวอย่างต่อไปนี้จะแสดงวิธีการ วัดหน่วยความจำโดยปราศจากอคติโดยใช้กระบวนการ Poisson ซึ่ง รับประกันว่าตัวอย่างมีแนวโน้มที่จะเกิดขึ้นในเวลาใดก็ได้อย่างเท่าเทียมกัน (การสาธิต แหล่งที่มา)

ขั้นแรก ให้กำหนดฟังก์ชันที่ตั้งเวลาการวัดหน่วยความจำครั้งถัดไปโดยใช้ 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() จะคำนวณช่วงเวลาแบบสุ่มเป็นมิลลิวินาที จนโดยเฉลี่ยแล้วจะมีการวัด 1 ครั้งทุก 5 นาที ดูเลขชี้กำลัง การกระจายถ้าคุณสนใจคณิตศาสตร์ที่อยู่เบื้องหลังฟังก์ชัน

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, หน้าต่างที่เกี่ยวข้อง และ Web Worker ทั้งหมดใน กระบวนการปัจจุบัน

รายการ breakdown ให้ข้อมูลเพิ่มเติมเกี่ยวกับหน่วยความจำที่ใช้ ชิ้น รายการอธิบายหน่วยความจำบางส่วนและระบุว่าเป็นชุด Windows, iframe และผู้ปฏิบัติงานที่ระบุโดย URL รายการในช่อง types ประเภทหน่วยความจำเฉพาะการใช้งานที่เชื่อมโยงกับหน่วยความจำ

คุณต้องจัดการกับรายการทั้งหมดในลักษณะทั่วไปและไม่ใช่ฮาร์ดโค้ด ตามเบราว์เซอร์ที่เฉพาะเจาะจง ตัวอย่างเช่น บางเบราว์เซอร์อาจ แสดงผล breakdown ที่ว่างเปล่าหรือ attribution ที่ว่างเปล่า เบราว์เซอร์อื่นๆ อาจ แสดงผลหลายรายการใน attribution ซึ่งบ่งชี้ว่าไม่สามารถแยก ว่ารายการใดเป็นเจ้าของความทรงจำ

ความคิดเห็น

กลุ่มชุมชนประสิทธิภาพเว็บและทีมงาน Chrome ขอขอบคุณ เพื่อรับฟังความคิดเห็นและประสบการณ์ของคุณเกี่ยวกับ performance.measureUserAgentSpecificMemory()

บอกเราเกี่ยวกับการออกแบบ API

มีบางอย่างเกี่ยวกับ API ที่ไม่ทำงานตามที่คาดไว้หรือไม่ หรือมี ที่ขาดคุณสมบัติที่คุณจำเป็นต้องนำไอเดียของคุณไปปฏิบัติ ยื่นปัญหาเกี่ยวกับข้อกำหนด performance.measureUserAgentSpecificMemory() ที่เก็บ GitHub หรือเพิ่ม ความคิดของคุณถึงปัญหาที่มีอยู่

รายงานปัญหาเกี่ยวกับการติดตั้งใช้งาน

คุณพบข้อบกพร่องในการติดตั้งใช้งาน Chrome ไหม หรือเป็นการติดตั้งใช้งาน แตกต่างจากข้อกำหนด รายงานข้อบกพร่องที่ new.crbug.com อย่าลืม ระบุรายละเอียดให้มากที่สุดเท่าที่จะทำได้ ให้คำแนะนำง่ายๆ ในการทำซ้ำ และตั้งค่าคอมโพเนนต์เป็น Blink>PerformanceAPIs ภาพ Glitch เหมาะสำหรับการแชร์ซ้ำที่ง่ายและรวดเร็ว

แสดงการสนับสนุน

คุณวางแผนที่จะใช้ performance.measureUserAgentSpecificMemory() ไหม การสนับสนุนแบบสาธารณะ ช่วยทีม Chrome จัดลำดับความสำคัญของฟีเจอร์ต่างๆ และแสดงให้ผู้ให้บริการเบราว์เซอร์รายอื่นๆ เห็นว่า ที่สำคัญคือต้องสนับสนุนพวกเขา ส่งทวีตไปยัง @ChromiumDev และแจ้งให้เราทราบถึงตำแหน่งและวิธีที่คุณใช้งาน

ลิงก์ที่มีประโยชน์

กิตติกรรมประกาศ

ขอขอบคุณอย่างยิ่งสำหรับ Domenic Denicola, Yoav Weiss และ Mathias Bynens สำหรับการตรวจสอบการออกแบบ API และ Dominik Inführ, Hannes Payer, Kentaro Hara, Michael Lippautz สำหรับการตรวจสอบโค้ด ใน Chrome ผมขอขอบคุณ Per Parker, Philipp Weis, Olga Belomestnykh และ Matthew Bolohan และ Neil Mckay ในการให้ความคิดเห็นอันมีค่าแก่ผู้ใช้ ซึ่ง ได้ปรับปรุง API

รูปภาพหลักโดย Harrison Broadbent ใน Unsplash