Pelajari cara mengukur penggunaan memori halaman web Anda dalam produksi untuk mendeteksi regresi.
Browser mengelola memori halaman web secara otomatis. Kapan pun halaman web membuat objek, {i>browser<i} mengalokasikan sejumlah memori "di balik layar" dapat menyimpan objek. Karena memori adalah sumber daya yang terbatas, browser melakukan pembersihan sampah memori untuk mendeteksi ketika sebuah objek tidak lagi diperlukan dan potongan memori yang mendasarinya.
Deteksinya tidak sempurna, dan terbukti bahwa deteksi yang sempurna adalah tugas yang mustahil. Oleh karena itu, browser mendekati gagasan "sebuah objek diperlukan" dengan gagasan "suatu objek dapat dijangkau". Jika laman web tidak dapat untuk menjangkau objek melalui variabelnya dan ruang isian dari objek lain yang dapat dijangkau, maka {i>browser<i} dapat mengklaim kembali objek tersebut dengan aman. Perbedaan antara ketiga dua gagasan 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 lagi
mengklaimnya kembali karena masih dapat dijangkau melalui object.b
di callback. Jadi
memori dari {i>array<i} yang
lebih besar bocor.
Kebocoran memori umum terjadi di Web. Sangat mudah untuk memperkenalkannya dengan lupa membatalkan pendaftaran pemroses peristiwa, dengan secara tidak sengaja mengambil objek dari iframe, dengan tidak menutup pekerja, mengumpulkan objek dalam array, dan seterusnya. Jika sebuah laman web mengalami kebocoran memori, maka penggunaan memorinya bertambah dari waktu ke waktu dan laman web muncul lambat dan membengkak bagi pengguna.
Langkah pertama untuk menyelesaikan masalah ini adalah mengukurnya. Yang baru
performance.measureUserAgentSpecificMemory()
API memungkinkan developer
mengukur penggunaan memori halaman web mereka dalam produksi dan dengan demikian
kebocoran yang lolos pengujian lokal.
Apa perbedaan performance.measureUserAgentSpecificMemory()
dengan API performance.memory
lama?
Jika Anda sudah memahami performance.memory
API non-standar yang ada,
Anda mungkin bertanya-tanya apa
perbedaan antara API baru ini. Perbedaan utamanya adalah
API lama menampilkan ukuran heap JavaScript sedangkan API baru
memperkirakan memori yang
digunakan oleh laman web. Perbedaan ini menjadi
penting saat Chrome berbagi heap yang sama dengan beberapa laman web (atau
beberapa halaman web yang sama). Dalam kasus tersebut, hasil dari
API dapat dinonaktifkan secara arbitrer. Karena API lama
ditentukan dalam
implementasi dari istilah spesifik seperti "heap", menstandarkannya tidak ada harapan.
Perbedaan lainnya adalah API baru melakukan pengukuran memori selama pembersihan sampah memori. Ini mengurangi noise dalam hasil, tetapi mungkin diperlukan sementara hingga hasilnya dibuat. Perhatikan bahwa browser lain mungkin memutuskan untuk mengimplementasikan API baru tanpa bergantung pada pembersihan sampah memori.
Kasus penggunaan yang disarankan
Penggunaan memori halaman web bergantung pada waktu peristiwa, tindakan pengguna, dan pembersihan sampah memori. Itulah mengapa API pengukuran memori ditujukan untuk menggabungkan data penggunaan memori dari produksi. Hasil dari setiap panggilan 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 dampak memori dan mendeteksi kebocoran memori.
- Menghubungkan penggunaan memori dengan durasi sesi untuk memverifikasi ada tidaknya kebocoran memori.
- Menghubungkan penggunaan memori dengan metrik pengguna untuk memahami dampak penggunaan memori secara keseluruhan.
Kompatibilitas browser
Saat ini, API tersebut hanya didukung di browser berbasis Chromium, mulai Chrome 89. Tujuan hasil API sangat bergantung pada implementasi karena browser memiliki cara yang berbeda untuk merepresentasikan obyek dalam memori dan berbagai cara memperkirakan penggunaan memori. Browser dapat mengecualikan beberapa region memori dari {i>accounting<i} jika pencatatan yang tepat terlalu mahal atau tidak layak. Dengan demikian, hasil tidak dapat dibandingkan di seluruh browser. 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.
Pengujian ini mengandalkan isolasi lintas asal, yang dapat diaktifkan oleh halaman web
dengan menyetel 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 bahwa API tidak segera menyelesaikan promise hasil dan justru menunggu untuk pembersihan sampah memori berikutnya.
Memanggil API akan memaksa pembersihan sampah memori setelah beberapa waktu tunggu,
saat ini ditetapkan ke 20 detik, meskipun mungkin terjadi lebih cepat. Memulai Chrome dengan
Flag command line --enable-blink-features='ForceEagerMeasureMemory'
mengurangi
waktu tunggu ke nol dan berguna untuk
proses debug dan pengujian lokal.
Contoh
Penggunaan API yang direkomendasikan adalah untuk mendefinisikan
mengambil contoh penggunaan memori di seluruh halaman web dan mengirimkan hasilnya ke server
untuk agregasi dan analisis. Cara paling sederhana adalah mengambil sampel secara berkala, untuk
contoh setiap M
menit. Namun demikian, hal ini akan menimbulkan bias pada data
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 kecenderungan 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 Eksponensial
distribusi 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();
}
Terakhir, mulailah mengukur.
// Start measurements.
scheduleMeasurement();
Hasilnya akan 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']
},
],
}
Perkiraan total penggunaan memori ditampilkan di kolom bytes
. Nilai ini adalah
sangat bergantung pada implementasi dan tidak dapat dibandingkan di seluruh browser. Mungkin
bahkan berubah antara versi yang berbeda
dari {i>browser<i} yang sama. Nilainya mencakup
JavaScript dan memori DOM semua iframe, jendela terkait, dan pekerja web di
proses saat ini.
Daftar breakdown
memberikan informasi lebih lanjut tentang memori yang digunakan. Masing-masing
menjelaskan beberapa bagian memori
dan mengatribusikannya ke satu set
windows, iframe, dan worker yang diidentifikasi berdasarkan URL. Kolom types
mencantumkan
jenis memori khusus implementasi
yang terkait dengan memori.
Penting untuk memperlakukan semua daftar secara umum dan tidak melakukan hardcode
asumsi berdasarkan {i>browser<i} tertentu. Misalnya, beberapa {i>browser<i} mungkin
menampilkan breakdown
kosong atau attribution
kosong. Browser lain mungkin
menampilkan beberapa entri dalam attribution
yang menunjukkan bahwa mereka tidak dapat membedakan
entri mana yang
memiliki memori.
Masukan
Grup Komunitas Performa Web dan tim Chrome akan senang
untuk mendengar tentang pemikiran
dan pengalaman Anda dengan
performance.measureUserAgentSpecificMemory()
.
Beri tahu kami tentang desain API
Apakah ada sesuatu terkait API yang tidak berfungsi seperti yang diharapkan? Atau apakah ada properti yang hilang yang diperlukan untuk menerapkan ide Anda? Ajukan masalah spesifikasi di repo GitHub performance.measureUserAgentSpecificMemory() atau tambahkan pendapat Anda terhadap masalah yang ada.
Laporkan masalah terkait penerapan
Apakah Anda menemukan bug pada implementasi Chrome? Ataukah implementasi
berbeda dengan spesifikasi? Laporkan bug di new.crbug.com. Pastikan untuk
sertakan detail sebanyak mungkin, berikan petunjuk sederhana untuk mereproduksi
bug, dan menetapkan Komponen ke Blink>PerformanceAPIs
.
Glitch sangat cocok untuk membagikan repro dengan cepat dan mudah.
Tunjukkan dukungan
Apakah Anda berencana menggunakan performance.measureUserAgentSpecificMemory()
? Dukungan publik Anda
membantu tim Chrome memprioritaskan fitur dan menunjukkan kepada vendor browser lain cara
pentingnya mendukung mereka. Kirim tweet ke @ChromiumDev
dan beri tahu kami tempat serta
cara Anda menggunakannya.
Link bermanfaat
- Penjelasan
- Demo | Sumber demo
- Bug pelacakan
- Entri ChromeStatus.com
- Perubahan sejak Origin Trial API
- Penyelesaian Uji Coba Origin
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 yang telah memberikan masukan berharga pengguna yang luar biasa meningkatkan API.
Banner besar oleh Harrison Broadbent di Unsplash