Terminologi memori

Meggin Kearney
Meggin Kearney

Bagian ini menjelaskan istilah umum yang digunakan dalam analisis memori, dan berlaku untuk berbagai alat pembuatan profil memori untuk berbagai bahasa.

Istilah dan gagasan yang dijelaskan di sini merujuk ke Profiler Heap Chrome DevTools. Jika Anda pernah bekerja dengan Java, .NET, atau memory profiler lainnya, artikel ini mungkin dapat membantu Anda.

Ukuran objek

Bayangkan memori sebagai grafik yang berisi jenis primitif (seperti angka dan string) dan objek (array terkait). Grafik ini mungkin secara visual direpresentasikan sebagai grafik dengan sejumlah titik yang saling terhubung sebagai berikut:

Representasi visual dari memori

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 (GC).

Saat menggunakan Heap Profiler di DevTools (alat untuk menyelidiki masalah memori yang ditemukan di bagian "Profiles"), Anda mungkin akan melihat beberapa kolom informasi yang berbeda. Dua yang terlihat berbeda adalah Shallow Size dan Retained Size, tetapi apa yang diwakili oleh keduanya?

Ukuran Shallow dan Retained

Ukuran dangkal

Ini adalah ukuran memori yang dipegang oleh objek itu sendiri.

Objek JavaScript standar memiliki 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 pekerja khusus yang dimulai oleh halaman. Meskipun demikian, objek kecil pun dapat menyimpan banyak 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 dependensinya yang tidak dapat dijangkau dari root GC.

Root GC terdiri dari nama sebutan channel yang dibuat (baik lokal maupun global) saat membuat referensi dari kode native ke objek JavaScript di luar V8. Semua handle tersebut dapat ditemukan dalam cuplikan heap di GC roots > Handle scope dan GC roots > Global tuas. Menjelaskan handle dalam dokumentasi ini tanpa mempelajari detail implementasi browser bisa membingungkan. Root GC dan handle tidak perlu dikhawatirkan.

Ada banyak akar GC internal yang sebagian besar tidak menarik bagi pengguna. Dari sudut pandang aplikasi, ada beberapa jenis root berikut:

  • Objek global jendela (di setiap iframe). Terdapat kolom jarak di snapshot heap yang merupakan jumlah referensi properti di jalur penahan terpendek dari jendela.
  • Hierarki DOM dokumen yang terdiri dari semua node DOM asli yang dapat dijangkau dengan melintasi dokumen. Tidak semuanya memiliki wrapper JS, tetapi jika memiliki wrapper, wrapper akan hidup saat dokumen masih aktif.
  • Terkadang objek dapat dipertahankan oleh konteks debugger dan konsol DevTools (misalnya setelah evaluasi konsol). Buat snapshot heap dengan konsol jelas dan tanpa titik henti sementara aktif di debugger.

Grafik memori dimulai dengan root, yang mungkin berupa objek window browser atau objek Global modul Node.js. Anda tidak dapat mengontrol bagaimana objek root ini dikumpulkan.

Objek root tidak dapat dikontrol

Apa pun yang tidak dapat dijangkau dari akar akan dikumpulkan sampahnya.

Pohon penahan objek

Heap adalah jaringan objek yang saling terhubung. Dalam dunia matematika, struktur ini disebut grafik atau grafik memori. Grafik dibuat dari node yang terhubung melalui 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 hal menarik yang dapat kita lihat dalam rekaman Profiler Heap di bawah ini mencakup jarak: jarak dari root GC. Jika hampir semua objek dari jenis yang sama berada pada jarak yang sama, dan beberapa objek berada pada jarak yang lebih jauh, itu adalah sesuatu yang perlu diselidiki.

Jarak dari akar

Dominator

Objek dominator terdiri dari struktur pohon karena setiap objek memiliki tepat satu dominator. Dominator sebuah objek mungkin tidak memiliki referensi langsung ke objek yang didominasinya; yaitu, pohon dominator bukan pohon rentang grafik.

Pada diagram di bawah ini:

  • Simpul 1 mendominasi simpul 2
  • Simpul 2 mendominasi simpul 3, 4, dan 6
  • Simpul 3 mendominasi simpul 5
  • Simpul 5 mendominasi simpul 8
  • Simpul 6 mendominasi simpul 7

Struktur pohon dominator

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.

Ilustrasi dominator animasi

Detail V8

Saat membuat profil memori, akan sangat membantu untuk memahami mengapa snapshot heap terlihat unik. 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 (mis., 3,14159..)
  • Boolean (benar atau salah)
  • String (misalnya, 'Werner Heisenberg')

Mereka tidak bisa mereferensikan nilai lain dan selalu menjadi leaf atau simpul akhir.

Angka dapat disimpan sebagai:

  • nilai bilangan bulat 31-bit langsung yang disebut bilangan bulat kecil (SMI), atau
  • objek heap, yang disebut sebagai angka heap. Nomor heap digunakan untuk menyimpan nilai yang tidak sesuai dengan bentuk SMI, misalnya double, atau ketika nilai perlu dikotakkan, misalnya menyetel properti pada nilai.

String dapat disimpan di:

  • heap VM, atau
  • secara eksternal di memori render. 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, sehingga akan tetap hidup selama ada setidaknya satu referensi yang kuat padanya.

Objek native adalah segala hal lain yang tidak ada di heap JavaScript. Objek native, 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 penggabungan. Penggabungan konten string konv hanya terjadi saat 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 penyambungan. Jika nantinya Anda menggabungkan d dengan hasil tersebut, Anda akan mendapatkan string kontra lain ((a, b), d).

Array - Array adalah Objek dengan kunci numerik. Jenis data ini digunakan secara luas di VM V8 untuk menyimpan data dalam jumlah besar. Kumpulan 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 hanya ada sedikit properti, properti tersebut dapat disimpan secara internal dalam objek JavaScript itu sendiri.

Map-objek yang mendeskripsikan jenis objek dan tata letaknya. Misalnya, peta digunakan untuk mendeskripsikan hierarki objek implisit untuk akses properti yang cepat.

Grup objek

Setiap grup objek asli terdiri dari objek yang saling merujuk. Pertimbangkan, misalnya, subhierarki DOM tempat setiap node memiliki link ke induknya dan link ke turunan berikutnya dan seinduk berikutnya, sehingga membentuk grafik yang terhubung. Perhatikan bahwa objek native tidak direpresentasikan dalam heap JavaScript, itulah sebabnya objek tersebut berukuran nol. Sebagai gantinya, objek wrapper dibuat.

Setiap objek wrapper menyimpan referensi ke objek native yang sesuai, untuk mengalihkan perintah padanya. Pada gilirannya sendiri, grup objek menyimpan objek wrapper. Namun, hal ini tidak menghasilkan siklus yang tidak dapat dikumpulkan, karena GC cukup cerdas untuk melepaskan grup objek yang wrapper-nya tidak lagi direferensikan. Namun, lupa melepaskan satu wrapper akan menyimpan seluruh grup dan wrapper terkait.