Pelajari cara mengukur penggunaan memori halaman web Anda dalam produksi untuk mendeteksi regresi.
Browser mengelola memori halaman web secara otomatis. Setiap kali halaman web membuat objek, browser mengalokasikan sebagian memori "di balik layar" untuk menyimpan objek. Karena memori adalah resource yang terbatas, browser melakukan pembersihan sampah memori untuk mendeteksi kapan objek tidak lagi diperlukan dan untuk mengosongkan bagian memori yang mendasarinya.
Namun, deteksi ini tidak sempurna, dan telah terbukti bahwa deteksi sempurna adalah tugas yang mustahil. Oleh karena itu, browser memperkirakan gagasan "objek diperlukan" dengan gagasan "objek dapat dijangkau". Jika halaman web tidak dapat menjangkau objek melalui variabelnya dan kolom objek lain yang dapat dijangkau, browser dapat mengklaim kembali objek dengan aman. Perbedaan antara kedua konsep ini menyebabkan kebocoran memori seperti yang diilustrasikan oleh contoh berikut.
const object = {a: new Array(1000), b: new Array(2000)};
setInterval(() => console.log(object.a), 1000);
Di sini, array b
yang lebih besar 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. Anda dapat dengan mudah memperkenalkannya dengan lupa membatalkan pendaftaran pemroses peristiwa, dengan tidak sengaja mengambil objek dari iframe, dengan tidak menutup pekerja, dengan mengumpulkan objek dalam array, dan sebagainya. Jika halaman web mengalami kebocoran memori, penggunaan memorinya akan meningkat seiring waktu dan halaman web akan tampak lambat dan membengkak bagi pengguna.
Langkah pertama dalam memecahkan masalah ini adalah mengukurnya. performance.measureUserAgentSpecificMemory()
API
baru memungkinkan developer mengukur
penggunaan memori halaman web mereka dalam produksi, sehingga mendeteksi kebocoran
memori yang lolos dari pengujian lokal.
Apa perbedaan performance.measureUserAgentSpecificMemory()
dengan performance.memory
API lama?
Jika sudah terbiasa dengan performance.memory
API non-standar yang ada,
Anda mungkin bertanya-tanya apa perbedaan API baru dengan API tersebut. Perbedaan utamanya adalah
API lama menampilkan ukuran heap JavaScript, sedangkan API baru
memperkirakan memori yang digunakan oleh halaman web. Perbedaan ini menjadi
penting saat Chrome berbagi heap yang sama dengan beberapa halaman web (atau
beberapa instance halaman web yang sama). Dalam kasus tersebut, hasil API
lama mungkin dinonaktifkan secara sewenang-wenang. Karena API lama ditentukan dalam
istilah khusus implementasi seperti "heap", standardisasinya tidak akan berhasil.
Perbedaan lainnya adalah API baru melakukan pengukuran memori selama pembersihan sampah. Hal ini akan mengurangi derau dalam hasil, tetapi mungkin perlu waktu hingga hasil dihasilkan. Perhatikan bahwa browser lain dapat memutuskan untuk menerapkan API baru tanpa mengandalkan pembersihan sampah.
Kasus penggunaan yang disarankan
Penggunaan memori halaman web bergantung pada waktu peristiwa, tindakan pengguna, dan pembersihan sampah memori. Itulah sebabnya API pengukuran memori 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 mendeteksi kebocoran memori baru.
- Melakukan pengujian A/B pada fitur baru untuk mengevaluasi dampaknya terhadap memori dan mendeteksi kebocoran memori.
- Mengaitkan penggunaan memori dengan durasi sesi untuk memverifikasi adanya atau tidak adanya kebocoran memori.
- Menghubungkan penggunaan memori dengan metrik pengguna untuk memahami dampak penggunaan memori secara keseluruhan.
Kompatibilitas browser
Saat ini, API hanya didukung di browser berbasis Chromium, mulai dari Chrome 89. Hasil API sangat bergantung pada implementasi karena browser memiliki cara yang berbeda untuk merepresentasikan objek dalam memori dan cara yang berbeda untuk memperkirakan penggunaan memori. Browser dapat mengecualikan beberapa wilayah memori dari pencatatan jika pencatatan yang tepat terlalu mahal atau tidak memungkinkan. Oleh karena itu, hasil tidak dapat dibandingkan di seluruh browser. Anda hanya dapat membandingkan hasil untuk browser yang sama.
Menggunakan performance.measureUserAgentSpecificMemory()
Deteksi fitur
Fungsi performance.measureUserAgentSpecificMemory
tidak akan tersedia atau dapat
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, yang berarti bahwa API tidak langsung me-resolve promise hasil, tetapi menunggu pembersihan sampah berikutnya.
Memanggil API akan memaksa pembersihan sampah memori setelah beberapa waktu tunggu, 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 menjadi nol dan berguna untuk proses debug dan pengujian lokal.
Contoh
Penggunaan API yang direkomendasikan adalah menentukan monitor memori global yang mengambil sampel penggunaan memori dari seluruh halaman web dan mengirimkan hasilnya ke server untuk agregasi dan analisis. Cara termudah adalah mengambil sampel secara berkala, misalnya
setiap M
menit. Namun, hal itu akan menyebabkan 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 memastikan bahwa sampel memiliki kemungkinan yang sama untuk terjadi pada waktu apa pun (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 ini.
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
hasil, 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();
}
Terakhir, 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 penerapan dan tidak dapat dibandingkan di seluruh browser. Hal ini bahkan
dapat berubah di antara berbagai versi 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.
Penting untuk memperlakukan semua daftar dengan cara umum dan tidak melakukan hardcode
asumsi berdasarkan browser tertentu. Misalnya, beberapa browser mungkin
menampilkan breakdown
kosong atau attribution
kosong. Browser lain mungkin
menampilkan beberapa entri di attribution
yang menunjukkan bahwa browser tidak dapat membedakan
entri mana yang memiliki memori.
Masukan
Web Performance Community Group dan tim Chrome ingin
mendengar pendapat dan pengalaman Anda terkait
performance.measureUserAgentSpecificMemory()
.
Ceritakan kepada kami tentang desain API
Apakah ada sesuatu tentang API yang tidak berfungsi seperti yang diharapkan? Atau apakah ada properti yang hilang yang Anda perlukan untuk menerapkan ide Anda? Ajukan masalah spesifikasi di repo GitHub performance.measureUserAgentSpecificMemory() atau tambahkan pendapat Anda ke masalah yang ada.
Melaporkan masalah terkait penerapan
Apakah Anda menemukan bug pada penerapan Chrome? Atau apakah penerapannya
berbeda dengan spesifikasinya? Laporkan bug di new.crbug.com. Pastikan untuk
menyertakan detail sebanyak mungkin, berikan petunjuk sederhana untuk mereproduksi
bug, dan tetapkan Components ke Blink>PerformanceAPIs
.
Glitch sangat cocok untuk membagikan rekaman ulang yang cepat dan mudah.
Menampilkan dukungan
Apakah Anda 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
- Penjelas
- Demo | Sumber demo
- Melacak bug
- Entri ChromeStatus.com
- Perubahan sejak Origin Trial API
- Uji Coba Origin Selesai
Ucapan terima kasih
Terima kasih banyak kepada Domenic Denicola, Yoav Weiss, Mathias Bynens atas peninjauan desain API, dan Dominik Inführ, Hannes Payer, Kentaro Hara, Michael Lippautz atas peninjauan kode di Chrome. Kami 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