Pelajari cara merekam snapshot heap dengan Memori > Profil > Snapshot heap dan menemukan kebocoran memori.
Heap profiler menampilkan distribusi memori berdasarkan objek JavaScript halaman Anda dan node DOM terkait. Gunakan untuk mengambil snapshot heap JS, menganalisis grafik memori, membandingkan snapshot, dan menemukan kebocoran memori. Untuk informasi selengkapnya, lihat Hierarki penahan objek.
Jepret
Untuk mengambil snapshot heap:
- Di halaman yang ingin Anda buat profilnya, buka DevTools lalu buka panel Memory.
- Pilih jenis pembuatan profil Heap snapshot, lalu pilih instance JavaScript VM, lalu klik Take snapshot.
Saat panel Memory memuat dan mengurai snapshot, panel ini akan menampilkan ukuran total objek JavaScript yang dapat dijangkau di bawah judul snapshot di bagian HEAP SNAPSHOTS.
Snapshot hanya menampilkan objek dari grafik memori yang dapat dijangkau dari objek global. Mengambil snapshot selalu dimulai dengan pembersihan sampah memori.
Hapus snapshot
Untuk menghapus semua snapshot, klik
Hapus semua profil:Lihat snapshot
Untuk memeriksa snapshot dari berbagai perspektif untuk tujuan yang berbeda, pilih salah satu tampilan dari menu drop-down di bagian atas:
Lihat | Konten | Tujuan |
---|---|---|
Ringkasan | Objek yang dikelompokkan menurut nama konstruktor. | Gunakan ini untuk mencari objek dan penggunaan memorinya berdasarkan jenis. Berguna untuk melacak kebocoran DOM. |
Perbandingan | Perbedaan antara dua snapshot. | Gunakan untuk membandingkan dua (atau lebih) snapshot, sebelum dan sesudah operasi. Konfirmasi keberadaan dan penyebab kebocoran memori dengan memeriksa delta di memori yang dibebaskan dan jumlah referensi. |
Pembatasan | Konten heap | Memberikan tampilan struktur objek yang lebih baik, dan membantu menganalisis objek yang dirujuk di namespace (jendela) global untuk menemukan apa yang mempertahankannya. Gunakan ini untuk menganalisis penutupan dan mengamati objek secara mendalam di tingkat rendah. |
Statistik | Diagram lingkaran alokasi memori | Lihat ukuran nyata dari bagian memori yang dialokasikan ke kode, string, array JS, array yang diketik, dan objek sistem. |
Tampilan ringkasan
Awalnya, snapshot heap akan terbuka di tampilan Summary yang mencantumkan Konstruktor di kolom. Anda dapat meluaskan konstruktor untuk melihat objek yang dibuat instance-nya.
Untuk memfilter konstruktor yang tidak relevan, ketik nama yang ingin Anda periksa di Filter class di bagian atas tampilan Ringkasan.
Angka di samping nama konstruktor menunjukkan jumlah total objek yang dibuat dengan konstruktor. Tampilan Ringkasan juga menampilkan kolom berikut:
- Jarak menunjukkan jarak ke root menggunakan jalur node sederhana terpendek.
- Shallow size menunjukkan jumlah ukuran dangkal semua objek yang dibuat oleh konstruktor tertentu. shallow size adalah ukuran memori yang disimpan oleh objek itu sendiri. Umumnya, array dan string memiliki ukuran dangkal yang lebih besar. Lihat juga Ukuran objek.
- Retained size menunjukkan ukuran yang dipertahankan maksimum di antara kumpulan objek yang sama. Retained size adalah ukuran memori yang dapat Anda kosongkan dengan menghapus objek dan membuat dependensinya tidak dapat dijangkau lagi. Lihat juga Ukuran objek.
Saat Anda meluaskan konstruktor, tampilan Summary akan menampilkan semua instance-nya. Setiap instance mendapatkan perincian ukuran dangkal dan ukuran yang dipertahankan di kolom yang sesuai. Angka setelah karakter @
adalah ID unik objek. Class ini memungkinkan Anda membandingkan snapshot heap per objek.
Filter konstruktor
Tampilan Summary memungkinkan Anda memfilter konstruktor berdasarkan kasus umum penggunaan memori yang tidak efisien.
Untuk menggunakan filter ini, pilih salah satu opsi berikut dari menu drop-down paling kanan di panel tindakan:
- All objek: semua objek yang diambil oleh snapshot saat ini. Tetapkan secara default.
- Objek yang dialokasikan sebelum snapshot 1: objek yang dibuat dan tetap berada di memori sebelum snapshot pertama diambil.
- Objek yang dialokasikan antara Snapshot 1 dan Snapshot 2: melihat perbedaan objek antara snapshot terbaru dan snapshot sebelumnya. Setiap snapshot baru menambahkan kenaikan filter ini ke menu drop-down.
- String duplikat: nilai string yang telah disimpan beberapa kali di memori.
- Objek yang dipertahankan oleh node yang terpisah: objek yang tetap aktif karena node DOM yang dilepas mereferensikannya.
- Objek yang dipertahankan oleh konsol DevTools: objek yang disimpan di memori karena objek tersebut dievaluasi atau berinteraksi melalui konsol DevTools.
Entri khusus di Ringkasan
Selain mengelompokkan berdasarkan konstruktor, tampilan Summary juga mengelompokkan objek berdasarkan:
- Fungsi bawaan seperti
Array
atauObject
. - Elemen HTML yang dikelompokkan menurut tagnya, misalnya,
<div>
,<a>
,<img>
, dan lainnya. - Fungsi yang Anda tetapkan dalam kode.
- Kategori khusus yang tidak didasarkan pada konstruktor.
(array)
Kategori ini mencakup berbagai objek seperti array internal yang tidak secara langsung sesuai dengan objek yang terlihat di JavaScript.
Misalnya, konten objek Array
JavaScript disimpan dalam objek internal sekunder bernama (object elements)[]
untuk memudahkan pengubahan ukuran. Demikian pula, properti bernama dalam objek JavaScript sering disimpan dalam objek internal sekunder bernama (object properties)[]
yang juga tercantum dalam kategori (array)
.
(compiled code)
Kategori ini mencakup data internal yang diperlukan V8 agar dapat menjalankan fungsi yang ditentukan oleh JavaScript atau WebAssembly. Setiap fungsi dapat ditampilkan dalam berbagai cara, dari kecil dan lambat hingga besar dan cepat.
V8 secara otomatis mengelola penggunaan memori dalam kategori ini. Jika suatu fungsi dijalankan berkali-kali, V8 akan menggunakan lebih banyak memori untuk fungsi tersebut agar fungsi dapat berjalan lebih cepat. Jika suatu fungsi tidak berjalan selama beberapa waktu, V8 dapat menghapus data internal untuk fungsi tersebut.
(concatenated string)
Jika V8 menyambungkan dua string, seperti dengan operator +
JavaScript, mungkin memilih untuk menampilkan hasil secara internal sebagai "string gabungan" yang juga dikenal sebagai struktur data Tali.
Alih-alih menyalin semua karakter dari dua string sumber ke dalam string baru, V8 mengalokasikan objek kecil dengan kolom internal yang disebut first
dan second
, yang mengarah ke dua string sumber. Ini memungkinkan V8 menghemat waktu dan memori. Dari perspektif kode JavaScript, ini hanyalah string normal, dan berperilaku seperti string lainnya.
InternalNode
Kategori ini mewakili objek yang dialokasikan di luar V8, seperti objek C++ yang ditentukan oleh Blink.
Untuk melihat nama class C++, gunakan Chrome for Testing, lalu lakukan langkah berikut:
- Buka DevTools dan aktifkan Setelan > Eksperimen > Tampilkan opsi untuk menampilkan data internal dalam cuplikan heap.
- Buka panel Memory, pilih Heap snapshot, lalu aktifkan expose internal (termasuk detail khusus implementasi tambahan).
- Rekonstruksi masalah yang menyebabkan
InternalNode
mempertahankan banyak memori. - Ambil cuplikan heap. Dalam snapshot ini, objek memiliki nama class C++, bukan
InternalNode
.
(object shape)
Seperti yang dijelaskan dalam Properti Cepat di V8, V8 melacak class tersembunyi (atau bentuk) sehingga beberapa objek dengan properti yang sama dalam urutan yang sama dapat direpresentasikan secara efisien. Kategori ini berisi class tersembunyi tersebut, yang disebut system / Map
(tidak terkait dengan JavaScript Map
), dan data terkait.
(sliced string)
Saat V8 perlu mengambil substring, seperti saat kode JavaScript memanggil String.prototype.substring()
, V8 dapat memilih untuk mengalokasikan objek string yang terpotong, bukan menyalin semua karakter yang relevan dari string asli. Objek baru ini berisi pointer ke string asli dan menjelaskan rentang karakter mana dari string asli yang akan digunakan.
Dari perspektif kode JavaScript, ini hanyalah string normal, dan berperilaku seperti string lainnya. Jika string yang dipotong mempertahankan banyak memori, program mungkin telah memicu Masalah 2869 dan mungkin dapat memanfaatkan langkah-langkah yang disengaja untuk "meratakan" string irisan.
system / Context
Objek internal jenis system / Context
berisi variabel lokal dari penutupan—cakupan JavaScript yang dapat diakses oleh fungsi bertingkat.
Setiap instance fungsi berisi pointer internal ke Context
tempat instance fungsi dijalankan, sehingga dapat mengakses variabel tersebut. Meskipun objek Context
tidak terlihat langsung dari JavaScript, Anda memiliki kontrol langsung terhadapnya.
(system)
Kategori ini berisi berbagai objek internal yang belum (belum) dikategorikan dalam cara yang lebih bermakna.
Tampilan perbandingan
Tampilan Perbandingan memungkinkan Anda menemukan objek yang bocor dengan membandingkan beberapa snapshot satu sama lain. Misalnya, melakukan suatu tindakan dan membalikkannya, seperti membuka dokumen dan menutupnya, seharusnya tidak meninggalkan objek tambahan.
Untuk memverifikasi bahwa operasi tertentu tidak menyebabkan kebocoran:
- Ambil snapshot heap sebelum melakukan operasi.
- Melakukan operasi. Artinya, berinteraksi dengan halaman dengan cara yang menurut Anda dapat menyebabkan kebocoran.
- Lakukan operasi pembalikan. Artinya, lakukan interaksi yang berlawanan dan ulangi beberapa kali.
- Ambil snapshot heap kedua, lalu ubah tampilannya ke Comparison, lalu bandingkan dengan Snapshot 1.
Tampilan Comparison menunjukkan perbedaan antara dua snapshot. Saat memperluas total entri, instance objek yang ditambahkan dan dihapus ditampilkan:
Tampilan Containment
Tampilan Containment adalah "tampilan menyeluruh" struktur objek aplikasi Anda. Ini memungkinkan Anda mengintip ke dalam penutupan fungsi, mengamati objek internal VM yang bersama-sama membentuk objek JavaScript, dan untuk memahami berapa banyak memori yang digunakan aplikasi Anda pada level yang sangat rendah.
Tampilan ini menyediakan beberapa titik entri:
- Objek DOMWindow. Objek global untuk kode JavaScript.
- GC root. Root GC yang digunakan oleh pembersih sampah memori VM. Root GC dapat terdiri dari peta objek bawaan, tabel simbol, stack thread VM, cache kompilasi, cakupan penanganan, dan handle global.
- Objek native. Objek browser "didorong" di dalam mesin virtual JavaScript untuk mengizinkan otomatisasi, misalnya, node DOM dan aturan CSS.
Bagian Retainer
Bagian Retainers di bagian bawah panel Memory menampilkan objek yang mengarah ke objek yang dipilih dalam tampilan. Panel Memory memperbarui bagian Retainers saat Anda memilih objek yang berbeda dalam tampilan mana pun, kecuali Statistik.
Dalam contoh ini, string yang dipilih dipertahankan oleh properti x
instance Item
.
Abaikan retainer
Anda dapat menyembunyikan penahan untuk mengetahui apakah objek lain mempertahankan objek yang dipilih. Dengan opsi ini, Anda tidak perlu menghapus retainer ini dari kode terlebih dahulu, lalu mengambil ulang snapshot heap.
Untuk menyembunyikan retainer, klik kanan dan pilih Ignore this retainer. Retainer yang diabaikan ditandai sebagai ignored
di kolom Distance. Untuk berhenti mengabaikan semua retensi, klik Restore ignored retainers di panel tindakan di bagian atas.
Menemukan objek tertentu
Untuk menemukan objek di heap yang dikumpulkan, Anda dapat menelusuri menggunakan Ctrl + F dan memasukkan ID objek.
Memberi nama fungsi untuk membedakan penutupan
Memberi nama fungsi akan sangat membantu sehingga Anda dapat membedakan penutupan dalam snapshot.
Misalnya, kode berikut tidak menggunakan fungsi bernama:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function() { // this is NOT a named function
return largeStr;
};
return lC;
}
Meskipun contoh ini menjalankan:
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function lC() { // this IS a named function
return largeStr;
};
return lC;
}
Mengungkap kebocoran DOM
Profiler heap mampu mencerminkan dependensi dua arah antara objek native browser (node DOM dan aturan CSS) dan objek JavaScript. Langkah ini membantu menemukan kebocoran tak terlihat yang terjadi karena subpohon DOM yang terpisah dan terlupakan yang melayang-layang.
Kebocoran DOM bisa lebih besar dari yang Anda kira. Perhatikan contoh berikut. Kapan sampah #tree
akan dibersihkan?
var select = document.querySelector;
var treeRef = select("#tree");
var leafRef = select("#leaf");
var body = select("body");
body.removeChild(treeRef);
//#tree can't be GC yet due to treeRef
treeRef = null;
//#tree can't be GC yet due to indirect
//reference from leafRef
leafRef = null;
//#NOW #tree can be garbage collected
#leaf
mempertahankan referensi ke induknya (parentNode
) dan secara rekursif hingga #tree
, sehingga hanya
saat leafRef
dibatalkan adalah pohon seluruh di bawah #tree
menjadi kandidat untuk GC.