Memantau total penggunaan memori halaman web Anda dengan measureUserAgentSpecificMemory()

Pelajari cara mengukur penggunaan memori halaman web Anda dalam produksi untuk mendeteksi regresi.

Brian Kenny
Brendan Kenny
Ulan Degenbaev
Ulan Degenbaev

Browser mengelola memori halaman web secara otomatis. Setiap kali halaman web membuat objek, browser mengalokasikan sepotong memori "di balik layar" untuk menyimpan objek. Karena memori adalah resource terbatas, browser melakukan pembersihan sampah memori untuk mendeteksi kapan sebuah objek tidak lagi diperlukan dan untuk membebaskan potongan memori yang mendasarinya.

Namun, deteksinya tidak sempurna, dan terbukti bahwa deteksi yang sempurna adalah tugas yang mustahil. Oleh karena itu, browser memperkirakan gagasan "objek diperlukan" dengan konsep "objek dapat dijangkau". Jika halaman web tidak dapat menjangkau objek melalui variabelnya dan kolom objek lain yang dapat dijangkau, browser dapat mengklaim kembali objek tersebut dengan aman. Perbedaan antara kedua konsep ini menyebabkan kebocoran memori seperti yang ditunjukkan oleh contoh berikut.

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

Di sini, array yang lebih besar b tidak lagi diperlukan, tetapi browser tidak mengklaimnya kembali karena masih dapat dijangkau melalui object.b dalam callback. Dengan demikian, memori array yang lebih besar akan bocor.

Kebocoran memori umum terjadi di Web. Sangat mudah untuk memperkenalkannya karena lupa membatalkan pendaftaran pemroses peristiwa, dengan secara tidak sengaja mengambil objek dari iframe, tidak menutup worker, dengan mengumpulkan objek dalam array, dan sebagainya. Jika halaman web mengalami kebocoran memori, penggunaan memorinya akan terus bertambah seiring waktu dan halaman web akan tampak lambat dan membengkak bagi pengguna.

Langkah pertama dalam mengatasi masalah ini adalah mengukurnya. performance.measureUserAgentSpecificMemory() API yang baru memungkinkan developer mengukur penggunaan memori halaman web mereka dalam produksi sehingga dapat mendeteksi kebocoran memori yang lolos dalam pengujian lokal.

Apa perbedaan performance.measureUserAgentSpecificMemory() dengan performance.memory API lama?

Jika sudah terbiasa dengan performance.memory API non-standar yang sudah ada, Anda mungkin ingin tahu apa perbedaan API baru dengannya. Perbedaan utamanya adalah API lama menampilkan ukuran heap JavaScript, sedangkan API baru memperkirakan memori yang digunakan oleh halaman web. Perbedaan ini menjadi penting jika Chrome membagikan heap yang sama dengan beberapa halaman web (atau beberapa instance halaman web yang sama). Dalam kasus seperti itu, hasil API lama dapat dinonaktifkan secara sewenang-wenang. Karena API lama didefinisikan dalam istilah khusus implementasi seperti "heap", proses menstandarkan API ini tidaklah mudah.

Perbedaan lainnya adalah API baru ini melakukan pengukuran memori selama pembersihan sampah memori. Tindakan ini akan mengurangi derau dalam hasil, tetapi mungkin perlu waktu beberapa saat sampai hasilnya muncul. Perhatikan bahwa browser lain mungkin memutuskan untuk mengimplementasikan API baru tanpa mengandalkan pembersihan sampah memori.

Kasus penggunaan yang disarankan

Penggunaan memori halaman web bergantung pada waktu peristiwa, tindakan pengguna, dan pembersihan sampah memori. Itulah sebabnya Memory Measurement API ditujukan untuk menggabungkan data penggunaan memori dari produksi. Hasil panggilan individual kurang berguna. Contoh kasus penggunaan:

  • Deteksi regresi selama peluncuran versi baru halaman web untuk menemukan kebocoran memori baru.
  • Melakukan pengujian A/B pada fitur baru untuk mengevaluasi dampak memorinya dan mendeteksi kebocoran memori.
  • Menghubungkan penggunaan memori dengan durasi sesi untuk memverifikasi ada atau tidaknya kebocoran memori.
  • Menghubungkan penggunaan memori dengan metrik pengguna untuk memahami dampak penggunaan memori secara keseluruhan.

Kompatibilitas browser

Dukungan Browser

  • 89
  • 89
  • x
  • x

Sumber

Saat ini, API tersebut hanya didukung di browser berbasis Chromium, mulai Chrome 89. Hasil API sangat bergantung pada implementasi karena browser memiliki berbagai cara untuk menampilkan objek dalam memori dan berbagai cara untuk memperkirakan penggunaan memori. Browser dapat mengecualikan beberapa region memori dari pencatatan jika penghitungan yang tepat terlalu mahal atau tidak memungkinkan. Dengan demikian, hasil tidak dapat dibandingkan di seluruh browser. Ini hanya berguna untuk membandingkan hasil untuk browser yang sama.

Menggunakan performance.measureUserAgentSpecificMemory()

Deteksi fitur

Fungsi performance.measureUserAgentSpecificMemory tidak akan tersedia atau mungkin gagal dengan SecurityError jika lingkungan eksekusi tidak memenuhi persyaratan keamanan untuk mencegah kebocoran informasi lintas origin. Fitur ini mengandalkan isolasi lintas origin, yang dapat diaktifkan halaman web dengan menetapkan header COOP+COEP.

Dukungan dapat dideteksi saat runtime:

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);
}

Pengujian lokal

Chrome melakukan pengukuran memori selama pembersihan sampah memori, yang berarti API tidak langsung menyelesaikan promise hasil, dan menunggu pembersihan sampah memori berikutnya.

Memanggil API akan memaksa pembersihan sampah memori setelah waktu tunggu beberapa saat, yang saat ini ditetapkan ke 20 detik, meskipun dapat terjadi lebih cepat. Memulai Chrome dengan tanda command line --enable-blink-features='ForceEagerMeasureMemory' akan mengurangi waktu tunggu hingga nol dan berguna untuk proses debug dan pengujian lokal.

Contoh

Penggunaan API yang direkomendasikan adalah menentukan pemantau memori global yang mengambil sampel penggunaan memori di seluruh halaman web dan mengirimkan hasilnya ke server untuk digabungkan dan dianalisis. Cara paling sederhana adalah membuat sampel secara berkala, misalnya setiap M menit. Namun, hal tersebut menimbulkan bias pada data karena puncak memori dapat terjadi di antara sampel.

Contoh berikut menunjukkan cara melakukan pengukuran memori yang tidak bias menggunakan proses Poisson, yang menjamin bahwa sampel memiliki kemungkinan yang sama untuk terjadi kapan saja (demo, sumber).

Pertama, tentukan fungsi yang menjadwalkan pengukuran memori berikutnya menggunakan setTimeout() dengan interval acak.

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);
}

Fungsi measurementInterval() menghitung interval acak dalam milidetik sehingga rata-rata ada satu pengukuran setiap lima menit. Lihat Distribusi eksponensial jika Anda tertarik dengan matematika di balik fungsi.

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

Terakhir, fungsi performMeasurement() asinkron memanggil API, mencatat hasilnya, dan menjadwalkan pengukuran berikutnya.

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();
}

Akhirnya, mulailah mengukur.

// Start measurements.
scheduleMeasurement();

Hasilnya mungkin terlihat seperti berikut:

// 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']
    },
  ],
}

Estimasi total penggunaan memori ditampilkan di kolom bytes. Nilai ini sangat bergantung pada implementasi dan tidak dapat dibandingkan di seluruh browser. Bahkan mungkin berubah antara versi berbeda dari browser yang sama. Nilai ini mencakup memori JavaScript dan DOM dari semua iframe, jendela terkait, dan pekerja web dalam proses saat ini.

Daftar breakdown memberikan informasi lebih lanjut tentang memori yang digunakan. Setiap entri menjelaskan beberapa bagian memori dan mengatribusikannya ke kumpulan jendela, iframe, dan pekerja yang diidentifikasi oleh URL. Kolom types mencantumkan jenis memori khusus implementasi yang terkait dengan memori.

Sebaiknya perlakukan semua daftar dengan cara umum dan jangan hardcode asumsi berdasarkan browser tertentu. Misalnya, beberapa browser mungkin menampilkan breakdown kosong atau attribution kosong. Browser lain mungkin menampilkan beberapa entri dalam attribution yang menunjukkan bahwa browser tidak dapat membedakan entri mana yang memiliki memori.

Masukan

Grup Komunitas Performa Web dan tim Chrome akan dengan senang hati mengetahui pendapat dan pengalaman Anda menggunakan performance.measureUserAgentSpecificMemory().

Beri tahu kami tentang desain API

Apakah ada sesuatu pada API yang tidak berfungsi seperti yang diharapkan? Atau apakah ada properti yang hilang yang Anda butuhkan untuk menerapkan ide Anda? Laporkan masalah spesifikasi pada repo GitHub performance.measureUserAgentSpecificMemory() atau tambahkan pendapat Anda ke masalah yang ada.

Melaporkan masalah terkait penerapan

Apakah Anda menemukan bug pada implementasi Chrome? Atau apakah implementasinya berbeda dengan spesifikasi? Laporkan bug di new.crbug.com. Pastikan untuk menyertakan detail sebanyak mungkin, memberikan petunjuk sederhana untuk mereproduksi bug, dan menyetel Komponen ke Blink>PerformanceAPIs. Glitch berfungsi dengan baik untuk berbagi repro yang cepat dan mudah.

Tampilkan dukungan

Berencana menggunakan performance.measureUserAgentSpecificMemory()? Dukungan publik Anda membantu tim Chrome memprioritaskan fitur dan menunjukkan kepada vendor browser lain betapa pentingnya mendukung fitur tersebut. Kirim tweet ke @ChromiumDev dan beri tahu kami tempat dan cara Anda menggunakannya.

Link bermanfaat

Ucapan terima kasih

Terima kasih banyak kepada Domenic Denicola, Yoav Weiss, Mathias Bynens untuk ulasan desain API, dan Dominik Inführ, Hannes Payer, Kentaro Hara, Michael Lippautz untuk peninjauan kode di Chrome. Saya juga berterima kasih kepada Per Parker, Philipp Weis, Olga Belomestnykh, Matthew Bolohan, dan Neil Mckay karena telah memberikan masukan pengguna yang berharga yang sangat meningkatkan API.

Banner besar oleh Harrison Broadbent di Unsplash