Pengumpulan sampah adalah mekanisme tersembunyi JavaScript
. Biasanya kita tidak perlu khawatir tentang pengumpulan sampah, kita hanya perlu fokus pada pengembangan fungsi. Namun ini tidak berarti kita bisa duduk santai saat menulis JavaScript
. Seiring dengan semakin kompleksnya fungsi yang kita terapkan dan jumlah kode yang terakumulasi, masalah kinerja menjadi semakin menonjol. Cara menulis kode yang dieksekusi lebih cepat dan menggunakan lebih sedikit memori adalah pekerjaan yang tiada habisnya bagi para programmer. Seorang programmer yang hebat selalu dapat mencapai hasil yang luar biasa dengan sumber daya yang sangat terbatas. Ini juga merupakan perbedaan antara makhluk biasa dan dewa yang menyendiri.
kode sampah? Saat dijalankan di memori komputer, semua variabel, objek, dan fungsi yang kita definisikan dalam kode akan menempati sejumlah ruang memori di dalam memori. Di komputer, ruang memori adalah sumber daya yang sangat ketat. Kita harus selalu memperhatikan penggunaan memori. Bagaimanapun, modul memori sangatlah mahal! Variabel, fungsi, atau objek dapat disebut sampah jika tidak lagi diperlukan untuk eksekusi kode selanjutnya setelah pembuatan.
Meskipun sangat mudah untuk memahami definisi sampah secara intuitif, namun untuk sebuah program komputer, sulit bagi kita untuk menyimpulkan pada saat tertentu bahwa variabel, fungsi, atau objek yang ada saat ini tidak akan digunakan lagi di masa mendatang. Untuk mengurangi biaya memori komputer dan memastikan eksekusi normal program komputer, kami biasanya menetapkan bahwa objek atau variabel yang memenuhi salah satu kondisi berikut adalah sampah:
variabel atau objek yang tidak direferensikan setara dengan rumah tanpa pintu. Meskipun benda-benda yang tidak dapat diakses telah dihubungkan, namun benda-benda tersebut tetap tidak dapat diakses dari luar sehingga tidak dapat digunakan kembali. Objek atau variabel yang memenuhi ketentuan di atas tidak akan pernah digunakan lagi pada eksekusi program di masa mendatang, sehingga dapat diperlakukan dengan aman sebagai pengumpulan sampah.
Jika kita memperjelas objek yang perlu dibuang melalui definisi di atas, apakah berarti tidak ada sampah pada variabel dan objek yang tersisa?
TIDAK! Sampah-sampah yang kami identifikasi saat ini hanya sebagian saja dari seluruh sampah. Masih ada sampah-sampah lain yang tidak memenuhi ketentuan di atas, namun tidak akan dimanfaatkan lagi.
Apakah sampah yang memenuhi definisi di atas dapat dikatakan sebagai "sampah absolut" dan sampah lain yang tersembunyi dalam program adalah "sampah relatif"?
Mekanisme pengumpulan sampah ( GC,Garbage Collection
) bertanggung jawab untuk mendaur ulang variabel yang tidak berguna dan ruang memori yang digunakan selama eksekusi program. Fenomena suatu objek masih ada di memori meskipun tidak ada kemungkinan untuk digunakan kembali disebut kebocoran memori . Kebocoran memori adalah fenomena yang sangat berbahaya, terutama pada program yang berjalan lama. Jika suatu program mengalami kebocoran memori, maka program tersebut akan menempati lebih banyak ruang memori hingga kehabisan memori.
String, objek, dan array tidak memiliki ukuran tetap, sehingga alokasi penyimpanan dinamis untuk objek tersebut hanya dapat dilakukan jika ukurannya diketahui. Setiap kali program JavaScript membuat string, array, atau objek, interpreter mengalokasikan memori untuk menyimpan entitas. Setiap kali memori dialokasikan secara dinamis seperti ini, memori tersebut pada akhirnya harus dikosongkan agar dapat digunakan kembali; jika tidak, penerjemah JavaScript akan menggunakan semua memori yang tersedia di sistem, menyebabkan sistem mogok.
Mekanisme pengumpulan sampah JavaScript
akan sesekali memeriksa variabel dan objek yang tidak berguna (sampah) dan melepaskan ruang yang ditempatinya.
Bahasa pemrograman yang berbeda mengadopsi strategi pengumpulan sampah yang berbeda. Misalnya, C++
tidak memiliki mekanisme pengumpulan sampah. Semua manajemen memori bergantung pada keterampilan programmer sendiri, yang membuat C++
lebih sulit untuk dikuasai. JavaScript
menggunakan keterjangkauan untuk mengelola memori. Secara harfiah, keterjangkauan berarti dapat dijangkau, yang berarti bahwa program dapat mengakses dan menggunakan variabel dan objek dengan cara tertentu. Memori yang ditempati oleh variabel-variabel ini tidak dapat dilepaskan.
JavaScript
menentukan sekumpulan nilai inheren yang dapat dijangkau, dan nilai dalam kumpulan tersebut secara inheren dapat dijangkau:
disebut root , yang merupakan node teratas dari pohon keterjangkauan;
Suatu variabel atau objek dianggap dapat dijangkau jika digunakan secara langsung atau tidak langsung oleh variabel root.
Dengan kata lain, suatu nilai dapat dijangkau jika dapat dijangkau melalui root (misalnya Abcde
).
biarkan orang = { anak laki-laki:{ anak laki-laki1:{nama:'xiaoming'}, cowok2:{nama:'xiaojun'}, }, cewek-cewek:{ cewek1:{nama:'xiaohong'}, cewek2:{nama:'huahua'}, }};
Kode di atas membuat sebuah objek dan menugaskannya ke variabel people
. Variabel people
berisi dua objek, boys
dan girls
, dan boys
dan girls
masing-masing berisi dua subobjek. Ini juga menciptakan struktur data yang berisi 3
tingkat hubungan referensi (terlepas dari tipe data dasar), seperti yang ditunjukkan di bawah ini:
Diantaranya, simpul people
secara alami dapat dijangkau karena merupakan variabel global. Node boys
dan girls
dapat dijangkau secara tidak langsung karena mereka direferensikan langsung oleh variabel global. boys1
, boys2
, girls1
dan girls2
juga merupakan variabel yang dapat dijangkau karena secara tidak langsung digunakan oleh variabel global dan dapat diakses melalui people.boys.boys
.
Jika kita menambahkan kode berikut setelah kode di atas:
people.girls.girls2 = null; people.girls.girls1 = people.boys.boys2
Maka diagram hierarki referensi di atas akan menjadi seperti berikut:
Diantaranya, girls1
dan girls2
menjadi node yang tidak dapat dijangkau karena terputusnya koneksi dari node grils
, yang berarti mereka akan didaur ulang oleh mekanisme pengumpulan sampah.
Dan jika saat ini kita mengeksekusi kode berikut:
people.boys.boys2 = null;
maka diagram hierarki referensinya akan menjadi seperti berikut:
Saat ini, meskipun node boys
dan node boys2
terputus, karena hubungan referensi antara node boys2
dan node girls
, boys2
masih dapat dijangkau dan tidak akan didaur ulang oleh mekanisme pengumpulan sampah.
Diagram asosiasi di atas membuktikan mengapa nilai ekuivalen variabel global disebut root , karena dalam diagram asosiasi, nilai jenis ini biasanya muncul sebagai simpul akar dari pohon hubungan.
biarkan orang = { anak laki-laki:{ anak laki-laki1:{nama:'xiaoming'}, cowok2:{nama:'xiaojun'}, }, cewek-cewek:{ cewek1:{nama:'xiaohong'}, cewek2:{nama:'huahua'}, }};people.boys.boys2.girlfriend = orang.girls.girls1; //boys2 mengacu pada girls1people.girls.girls1.boyfriend = people.boys.boys2; //girls1 mengacu pada boys2.
Kode di atas menciptakan hubungan yang saling terkait antara boys2
dan girls1
.
Pada titik ini, jika kita memutus hubungan antara boys
dan boys2
:
hapus people.boys.boys2
diagram hubungan antar objek adalah sebagai berikut:
Jelasnya, tidak ada node yang tidak dapat dijangkau.
Pada titik ini, jika kita memutus hubungan hubungan boyfriend
:
people.girls.girls1
;
Saat ini, meskipun masih terdapat hubungan girlfriend
antara boys2
dan girls1
, boys2
menjadi simpul yang tidak dapat dijangkau dan akan diambil kembali oleh mekanisme pengumpulan sampah.
biarkan orang = { anak laki-laki:{ anak laki-laki1:{nama:'xiaoming'}, cowok2:{nama:'xiaojun'}, }, cewek-cewek:{ cewek1:{nama:'xiaohong'}, cewek2:{nama:'huahua'}, }};delete people.boys;delete people.girls
Diagram hierarki referensi yang dibentuk oleh kode di atas adalah sebagai berikut:
Saat ini, meskipun masih terdapat hubungan saling referensi antara objek-objek di dalam kotak putus-putus, objek-objek tersebut juga tidak dapat dijangkau dan akan dihapus oleh mekanisme pengumpulan sampah. Node-node ini telah kehilangan hubungannya dengan root dan menjadi tidak dapat dijangkau.
6.Yang disebut penghitungan referensi, seperti namanya, adalah penghitungan setiap kali suatu objek direferensikan, menambahkan referensi akan menambah satu, dan menghapus referensi akan menguranginya jika referensi angka menjadi 0, itu dianggap Sampah, sehingga menghapus objek untuk mendapatkan kembali memori.
Misalnya:
biarkan pengguna = {nama pengguna:'xiaoming'}; //Objek direferensikan oleh variabel pengguna, hitungan +1 biarkan pengguna2 = pengguna; //Objek direferensikan oleh variabel baru, dan hitungannya +1 pengguna = nol; //Variabel tidak lagi mengacu pada objek, hitungannya -1 pengguna2 = nol; //Variabel tidak lagi mengacu pada objek, bilangan ganjil -1 //Saat ini, jumlah referensi objek adalah 0 dan akan dihapus.
Meskipun metode penghitungan referensi tampaknya sangat masuk akal, pada kenyataannya, terdapat celah yang jelas dalam mekanisme daur ulang memori menggunakan metode penghitungan referensi.
Misalnya:
biarkan anak laki-laki = {}; biarkan gadis = {}; laki-laki.pacar = perempuan; cewek.pacar = cowok; anak laki-laki = nol; girl = null;
Kode di atas mempunyai referensi timbal balik antara boy
dan girl
. Penghitungan akan menghapus referensi di boy
dan girl
, dan kedua objek tersebut tidak akan didaur ulang. Karena adanya referensi melingkar, jumlah referensi dari dua objek anonim tidak akan pernah kembali ke nol, sehingga mengakibatkan kebocoran memori.
Ada konsep smart pointer ( shared_ptr
) di C++
. Pemrogram dapat menggunakan smart pointer untuk menggunakan destruktor objek untuk melepaskan jumlah referensi. Namun, kebocoran memori akan terjadi jika terjadi referensi melingkar.
Untungnya, JavaScript
telah mengadopsi strategi lain yang lebih aman, yang lebih menghindari risiko kebocoran memori.
Tandai mark and sweep
adalah algoritma pengumpulan sampah yang diadopsi oleh mesin JavaScript
. Prinsip dasarnya adalah memulai dari akar , melintasi terlebih dahulu hubungan referensi antar variabel, dan memberi tanda (优秀员工徽章
) pada variabel yang dilalui. Objek yang tidak ditandai akhirnya dihapus.
Proses dasar dari algoritme ini adalah sebagai berikut:
2
sampai tidak ada karyawan Unggul baru yang bergabung;Contoh:
Jika pada program kita terdapat hubungan referensi objek seperti gambar dibawah ini:
Kita dapat melihat dengan jelas bahwa ada "pulau yang dapat dijangkau" di sisi kanan seluruh gambar. Mulai dari akarnya , pulau tersebut tidak akan pernah bisa dijangkau. Namun pemulung tidak memiliki sudut pandang Tuhan seperti kita. Mereka hanya akan menandai simpul akar sebagai karyawan berprestasi berdasarkan algoritma.
Kemudian mulailah dari karyawan berprestasi dan temukan semua simpul yang dikutip oleh karyawan berprestasi, seperti tiga simpul dalam kotak putus-putus pada gambar di atas. Kemudian tandai node yang baru ditemukan sebagai karyawan berprestasi.
Proses pencarian dan penandaan diulangi hingga seluruh node yang ditemukan berhasil ditandai.
Akhirnya, efek yang ditunjukkan pada gambar di bawah ini tercapai:
Karena pulau-pulau di sebelah kanan masih belum ditandai setelah siklus eksekusi algoritma berakhir, node-node ini tidak akan dapat dijangkau oleh tugas pengumpul sampah dan pada akhirnya akan dibersihkan.
Anak-anak yang telah mempelajari struktur data dan algoritme mungkin akan terkejut saat mengetahui bahwa ini adalah traversal grafik, mirip dengan algoritme grafik terhubung.
Pengumpulan sampah adalah tugas berskala besar. Terutama ketika jumlah kode sangat besar, seringnya eksekusi algoritma pengumpulan sampah akan memperlambat eksekusi program secara signifikan. Algoritme JavaScript
telah melakukan banyak optimasi dalam pengumpulan sampah untuk memastikan bahwa program dapat dijalankan secara efisien sekaligus memastikan pelaksanaan pekerjaan daur ulang secara normal.
Strategi yang diadopsi untuk optimalisasi kinerja biasanya mencakup poin-poin berikut:
Program JavaScriptJavaScript
mempertahankan sejumlah besar variabel selama eksekusi, dan seringnya pemindaian variabel-variabel ini akan menyebabkan overhead yang signifikan. Namun variabel-variabel tersebut mempunyai karakteristik tersendiri dalam siklus hidupnya. Misalnya, variabel lokal sering dibuat, digunakan dengan cepat, dan kemudian dibuang, sedangkan variabel global menempati memori dalam waktu yang lama. JavaScript
mengelola dua jenis objek secara terpisah. Untuk variabel lokal yang dibuat, digunakan, dan dibuang dengan cepat, pengumpul sampah akan sering memindai untuk memastikan bahwa variabel ini segera dibersihkan setelah tidak dapat digunakan lagi. Untuk variabel yang menyimpan memori dalam waktu lama, kurangi frekuensi pemeriksaannya, sehingga menghemat sejumlah overhead.
Ide inkremental sangat umum dalam optimalisasi kinerja dan juga dapat digunakan untuk pengumpulan sampah. Ketika jumlah variabelnya sangat banyak, tentu sangat memakan waktu untuk menelusuri semua variabel sekaligus dan mengeluarkan nilai karyawan yang luar biasa, sehingga mengakibatkan kelambatan selama eksekusi program. Oleh karena itu, mesin akan membagi pekerjaan pengumpulan sampah menjadi beberapa subtugas, dan secara bertahap menjalankan setiap tugas kecil selama pelaksanaan program. Hal ini akan menyebabkan penundaan pemulihan tertentu, tetapi biasanya tidak akan menyebabkan kelambatan program yang jelas.
CPUCPU
selalu berfungsi bahkan dalam program yang kompleks. Hal ini terutama karena CPU
bekerja sangat cepat dan IO
periferal seringkali beberapa kali lipat lebih lambat. Oleh karena itu, sebaiknya atur strategi pengumpulan sampah saat CPU
berada menganggur. Ini adalah metode pengoptimalan kinerja yang sangat efektif dan pada dasarnya tidak akan menimbulkan efek buruk apa pun pada program itu sendiri. Strategi ini mirip dengan peningkatan waktu idle sistem, dan pengguna sama sekali tidak mengetahui eksekusi latar belakang.
Tugas utama artikel ini hanyalah mengakhiri mekanisme pengumpulan sampah, strategi yang umum digunakan, dan metode pengoptimalan. Artikel ini tidak dimaksudkan untuk memberikan pemahaman mendalam kepada semua orang tentang prinsip eksekusi latar belakang mesin.
Melalui artikel ini, Anda harus memahami:
JavaScript
, yang dijalankan di latar belakang dan tidak mengharuskan kita mengkhawatirkannya;