Bagian ini menjelaskan istilah umum yang digunakan dalam analisis memori, dan berlaku untuk berbagai alat pembuatan profil memori untuk berbagai bahasa.
Istilah dan konsep yang dijelaskan di sini merujuk pada Profiler Heap Chrome DevTools. Jika Anda pernah menggunakan Java, .NET, atau beberapa memory profiler lainnya, hal ini mungkin perlu diingat.
Ukuran objek
Bayangkan memori sebagai grafik dengan jenis primitif (seperti angka dan string) dan objek (array asosiatif). Diagram ini dapat direpresentasikan secara visual sebagai grafik dengan sejumlah titik yang saling terhubung sebagai berikut:
Objek dapat menyimpan memori dengan dua cara:
- Langsung oleh objek itu sendiri.
- Secara implisit dengan menyimpan referensi ke objek lain, sehingga mencegah objek tersebut dibuang secara otomatis oleh pembersih sampah memori (singkatnya GC).
Saat menggunakan Heap Profiler di DevTools (alat untuk menyelidiki masalah memori yang ditemukan di bagian "Profil"), Anda mungkin akan melihat beberapa kolom informasi yang berbeda. Dua yang menarik adalah Shallow Size dan Retained Size, tetapi apa yang diwakili oleh keduanya?
Ukuran dangkal
Ini adalah ukuran memori yang dipegang oleh objek itu sendiri.
Objek JavaScript standar memiliki beberapa memori yang dicadangkan untuk deskripsinya dan untuk menyimpan nilai langsung. Biasanya, hanya array dan string yang dapat memiliki ukuran dangkal yang signifikan. Namun, string dan array eksternal sering kali memiliki penyimpanan utamanya di memori perender, sehingga hanya mengekspos objek wrapper kecil pada heap JavaScript.
Memori perender adalah semua memori proses tempat halaman yang diperiksa dirender: memori native + memori heap JS halaman + memori heap JS semua worker khusus yang dimulai oleh halaman. Meskipun demikian, objek kecil pun dapat menyimpan sejumlah besar memori secara tidak langsung, dengan mencegah objek lain dibuang oleh proses pembersihan sampah memori otomatis.
Ukuran yang dipertahankan
Ini adalah ukuran memori yang dibebaskan setelah objek itu sendiri dihapus beserta objek dependen yang dibuat tidak dapat dijangkau dari GC roots.
GC root terdiri dari handle yang dibuat (baik lokal maupun global) saat membuat referensi dari kode native ke objek JavaScript di luar V8. Semua handle tersebut dapat ditemukan dalam snapshot heap di bagian GC roots > Handle scope dan GC roots > Global handle. Menjelaskan handle dalam dokumentasi ini tanpa mempelajari detail implementasi browser mungkin membingungkan. Root GC dan handle tidak perlu Anda khawatirkan.
Ada banyak akar GC internal yang sebagian besar tidak menarik bagi pengguna. Dari sudut pandang aplikasi, ada jenis root berikut:
- Objek global jendela (di setiap iframe). Terdapat kolom jarak di snapshot heap yang merupakan jumlah referensi properti di jalur penahanan terpendek dari jendela.
- Hierarki DOM dokumen yang terdiri dari semua node DOM native yang dapat dijangkau dengan menelusuri dokumen. Tidak semuanya memiliki wrapper JS, tetapi jika memiliki, wrapper akan aktif saat dokumen aktif.
- Terkadang objek dapat dipertahankan oleh konteks debugger dan konsol DevTools (mis., setelah evaluasi konsol). Buat snapshot heap dengan konsol bersih dan tanpa titik henti sementara aktif di debugger.
Grafik memori dimulai dengan root, yang mungkin berupa objek window
browser atau objek Global
dari modul Node.js. Anda tidak bisa mengontrol bagaimana objek root ini dikumpulkan sampahnya.
Apa pun yang tidak dapat dijangkau dari {i>root<i} akan dikumpulkan GC.
Hierarki penyimpanan objek
Heap adalah jaringan objek yang saling berhubungan. Dalam dunia matematika, struktur ini disebut grafik atau grafik memori. Grafik dibuat dari node yang dihubungkan menggunakan edge, yang keduanya diberi label.
- Node (atau objek) diberi label menggunakan nama fungsi konstruktor yang digunakan untuk membangunnya.
- Edge diberi label menggunakan nama properti.
Pelajari cara merekam profil menggunakan Heap Profiler. Beberapa informasi menarik yang dapat dilihat di rekaman Heap Profiler di bawah ini mencakup jarak: jarak dari root GC. Jika hampir semua objek dengan jenis yang sama berada pada jarak yang sama, dan beberapa objek berada pada jarak yang lebih besar, sebaiknya selidiki.
Dominator
Objek dominator terdiri dari struktur pohon karena setiap objek memiliki tepat satu dominator. Dominator suatu objek mungkin tidak memiliki referensi langsung ke objek yang didominasinya; yaitu, pohon dominator bukanlah pohon rentang grafik.
Dalam diagram di bawah:
- Simpul 1 mendominasi simpul 2
- Simpul 2 mendominasi node 3, 4, dan 6
- Node 3 mendominasi node 5
- Node 5 mendominasi node 8
- Simpul 6 mendominasi simpul 7
Pada contoh di bawah, node #3
adalah dominator #10
, tetapi #7
juga ada di setiap jalur sederhana
dari GC ke #10
. Oleh karena itu, objek B adalah dominator objek A jika B ada di setiap jalur sederhana
dari root ke objek A.
Detail V8
Saat membuat profil memori, sebaiknya pahami mengapa snapshot heap terlihat seperti itu. Bagian ini menjelaskan beberapa topik terkait memori yang secara khusus sesuai dengan mesin virtual JavaScript V8 (VM V8 atau VM).
Representasi objek JavaScript
Ada tiga jenis primitif:
- Angka (misalnya, 3.14159..)
- Boolean (benar atau salah)
- String (misalnya, 'Werner Heisenberg')
Elemen tersebut tidak dapat mereferensi nilai lain dan selalu berupa daun atau simpul akhir.
Nomor dapat disimpan sebagai:
- nilai bilangan bulat 31-bit langsung yang disebut bilangan bulat kecil (SMI), atau
- objek heap, yang disebut sebagai angka heap. Angka heap digunakan untuk menyimpan nilai yang tidak sesuai dengan bentuk SMI, misalnya doubles, atau ketika nilai perlu di-box, seperti menyetel properti pada nilai tersebut.
String dapat disimpan di:
- heap VM, atau
- secara eksternal di memori perender. Objek wrapper dibuat dan digunakan untuk mengakses penyimpanan eksternal tempat, misalnya, sumber skrip dan konten lain yang diterima dari Web disimpan, bukan disalin ke heap VM.
Memori untuk objek JavaScript baru dialokasikan dari heap JavaScript khusus (atau heap VM). Objek ini dikelola oleh pembersih sampah memori V8 dan dengan demikian, akan tetap aktif selama ada setidaknya satu referensi kuat ke objek tersebut.
Objek native adalah semua hal lain yang tidak ada dalam heap JavaScript. Objek native, yang berbeda dengan objek heap, tidak dikelola oleh pembersih sampah memori V8 selama masa aktifnya, dan hanya dapat diakses dari JavaScript menggunakan objek wrapper JavaScript-nya.
String Cons adalah objek yang terdiri dari pasangan string yang disimpan lalu digabungkan, dan merupakan hasil penyambungan. Penggabungan konten string konstanta hanya terjadi jika diperlukan. Contohnya adalah ketika substring dari string yang digabungkan perlu dibuat.
Misalnya, jika Anda menggabungkan a dan b, Anda akan mendapatkan string (a, b) yang mewakili hasil penggabungan. Jika Anda nantinya menggabungkan d dengan hasil tersebut, Anda akan mendapatkan string kelemahan lainnya ((a, b), d).
Array - Array adalah Objek dengan kunci numerik. Tabel ini digunakan secara ekstensif di VM V8 untuk menyimpan data dalam jumlah besar. Rangkaian key-value pair yang digunakan seperti kamus dicadangkan oleh array.
Objek JavaScript umum dapat berupa salah satu dari dua jenis array yang digunakan untuk menyimpan:
- properti bernama, dan
- elemen numerik
Jika jumlah properti sangat sedikit, properti tersebut dapat disimpan secara internal di objek JavaScript itu sendiri.
Peta - objek yang mendeskripsikan jenis objek dan tata letaknya. Misalnya, peta digunakan untuk menjelaskan hierarki objek implisit agar akses properti yang cepat.
Grup objek
Setiap grup objek native terdiri dari objek yang saling mereferensi. Pertimbangkan, misalnya, subhierarki DOM tempat setiap node memiliki link ke induknya dan menautkan ke turunan berikutnya dan seinduk berikutnya, sehingga membentuk grafik yang terhubung. Perlu diperhatikan bahwa objek native tidak ditampilkan di heap JavaScript, itulah sebabnya objek tersebut berukuran nol. Sebagai gantinya, objek wrapper dibuat.
Setiap objek wrapper menyimpan referensi ke objek native yang terkait, untuk mengalihkan perintah ke objek tersebut. Pada gilirannya sendiri, grup objek menyimpan objek wrapper. Namun, hal ini tidak menciptakan siklus yang tidak dapat dikumpulkan, karena GC cukup cerdas untuk melepaskan grup objek yang wrapper-nya tidak lagi direferensikan. Namun, lupa merilis satu wrapper akan menahan seluruh grup dan wrapper terkait.