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 sejumlah besar memori "di balik layar" untuk menyimpan objek tersebut. Karena memori adalah resource terbatas, browser melakukan pembersihan sampah memori untuk mendeteksi saat suatu objek tidak lagi diperlukan dan membebaskan potongan memori yang mendasarinya.
Namun, deteksi ini tidak sempurna, dan terbukti bahwa deteksi yang sempurna adalah tugas yang mustahil. Oleh karena itu, browser memperkirakan gagasan "suatu objek diperlukan" dengan gagasan "suatu objek dapat dijangkau". Jika halaman web tidak dapat menjangkau objek melalui variabelnya dan kolom objek yang dapat dijangkau lainnya, browser dapat mengklaim kembali objek tersebut dengan aman. Perbedaan antara kedua gagasan 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 mengklaim kembali karena masih dapat dijangkau melalui object.b
dalam callback. Dengan demikian,
memori array yang lebih besar bocor.
Kebocoran memori umum terjadi di Web. Anda dapat dengan mudah mengenalnya dengan lupa membatalkan pendaftaran pemroses peristiwa, dengan mengambil objek dari iframe secara tidak sengaja, tidak menutup pekerja, dengan mengumpulkan objek dalam array, dan seterusnya. Jika halaman web mengalami kebocoran memori, penggunaan memorinya akan meningkat seiring waktu dan halaman web tampak lambat dan membengkak bagi pengguna.
Langkah pertama untuk menyelesaikan masalah ini adalah mengukurnya. performance.measureUserAgentSpecificMemory()
API
yang baru memungkinkan developer
mengukur penggunaan memori halaman web mereka dalam produksi, sehingga mendeteksi kebocoran
memori yang lolos pengujian lokal.
Apa perbedaan performance.measureUserAgentSpecificMemory()
dengan API performance.memory
lama?
Jika sudah terbiasa dengan performance.memory
API non-standar yang sudah ada,
Anda mungkin bertanya-tanya apa perbedaan API yang 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 saat Chrome berbagi heap yang sama dengan beberapa halaman web (atau
beberapa instance halaman web yang sama). Dalam kasus seperti itu, hasil API lama
dapat dinonaktifkan secara bebas. Karena API lama didefinisikan dalam
istilah khusus implementasi seperti "heap", standarisasinya tidak ada gunanya.
Perbedaan lainnya adalah API baru melakukan pengukuran memori selama pembersihan sampah memori. Tindakan ini akan mengurangi derau dalam hasil, tetapi mungkin perlu waktu beberapa saat hingga hasilnya dibuat. Perhatikan bahwa browser lain mungkin memutuskan untuk menerapkan 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 mengapa API pengukuran memori ditujukan untuk menggabungkan data penggunaan memori dari production. 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. 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 region memori dari pencatatan jika penghitungan yang tepat terlalu mahal atau tidak memungkinkan. Oleh karena itu, hasil tidak dapat dibandingkan di berbagai browser. Hanya berguna untuk membandingkan hasil untuk {i>browser<i} 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.
Layanan ini mengandalkan isolasi lintas asal, 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 bahwa API tidak segera me-resolve promise hasil, dan justru menunggu pembersihan sampah memori berikutnya.
Memanggil API akan memaksa pembersihan sampah memori setelah beberapa waktu tunggu, yang saat ini
ditetapkan ke 20 detik, meskipun mungkin terjadi lebih cepat. Memulai Chrome dengan
flag 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 untuk menentukan monitor memori global yang
mengambil sampel penggunaan memori seluruh halaman web dan mengirimkan hasilnya ke server
untuk digabungkan dan dianalisis. Cara paling sederhana adalah dengan mengambil sampel secara berkala, misalnya setiap M
menit. Namun, hal tersebut akan 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 cenderung sama-sama 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 ingin mengetahui 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 akan 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
sangat bergantung pada implementasi dan tidak dapat dibandingkan di berbagai browser. Bahkan dapat berubah antara versi yang berbeda dari browser yang sama. Nilai ini mencakup JavaScript dan memori DOM 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 serangkaian jendela, iframe, dan worker yang diidentifikasi oleh 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 browser tertentu. Misalnya, beberapa browser mungkin
menampilkan breakdown
kosong atau attribution
kosong. Browser lain dapat
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 ingin mengetahui pendapat dan pengalaman Anda terkait 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 perlu untuk menerapkan ide Anda? Ajukan masalah spesifikasi di repo GitHub performance.measureUserAgentSpecificMemory() atau tambahkan pendapat Anda ke masalah yang sudah ada.
Laporkan masalah terkait penerapan
Apakah Anda menemukan bug pada implementasi Chrome? Atau apakah implementasinya
berbeda dengan spesifikasi? Laporkan bug di new.crbug.com. Pastikan Anda menyertakan detail sebanyak mungkin, memberikan petunjuk sederhana untuk mereproduksi bug, dan menyetel 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 lainnya
betapa pentingnya mendukung mereka. Kirim tweet ke @ChromiumDev
dan beri tahu kami tempat dan 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 peninjauan 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 kualitas API.
Banner besar oleh Harrison Broadbent di Unsplash