Apa itu pengumpulan sampah JavaScript?

Jawaban:

192

Eric Lippert menulis posting blog rinci tentang hal ini beberapa waktu lalu (selain membandingkannya dengan VBScript ). Lebih tepatnya, ia menulis tentang JScript , yang merupakan implementasi ECMAScript dari Microsoft sendiri, meskipun sangat mirip dengan JavaScript. Saya akan membayangkan bahwa Anda dapat mengasumsikan sebagian besar perilaku akan sama untuk mesin JavaScript Internet Explorer. Tentu saja, implementasinya akan bervariasi dari peramban ke peramban, meskipun saya menduga Anda dapat mengambil sejumlah prinsip umum dan menerapkannya ke peramban lain.

Dikutip dari halaman itu:

JScript menggunakan tanda dan penyapu sampah nenerenerasi. Ini berfungsi seperti ini:

  • Setiap variabel yang "dalam lingkup" disebut "pemulung". Seorang pemulung dapat merujuk pada angka, objek, string, apa pun. Kami mempertahankan daftar pemulung - variabel dipindahkan ke daftar pemulung saat mereka masuk ke ruang lingkup dan keluar dari daftar pemulung saat mereka keluar dari ruang lingkup.

  • Sesekali pengumpul sampah berjalan. Pertama itu menempatkan "tanda" pada setiap objek, variabel, string, dll - semua memori dilacak oleh GC. (JScript menggunakan struktur data VARIAN secara internal dan ada banyak bit tambahan yang tidak digunakan dalam struktur itu, jadi kami hanya menetapkan salah satunya.)

  • Kedua, membersihkan tanda pada pemulung dan penutupan transitif referensi pemulung. Jadi jika objek scavenger mereferensikan objek nonscavenger maka kita menghapus bit pada nonscavenger, dan pada semua yang dimaksud. (Saya menggunakan kata "penutupan" dalam arti yang berbeda dari pada posting saya sebelumnya.)

  • Pada titik ini kita tahu bahwa semua memori yang masih ditandai adalah memori yang dialokasikan yang tidak dapat dijangkau oleh jalur apa pun dari variabel dalam-lingkup apa pun. Semua benda itu diinstruksikan untuk menghancurkan dirinya sendiri, yang menghancurkan referensi melingkar.

Tujuan utama pengumpulan sampah adalah agar pemrogram tidak perlu khawatir tentang manajemen memori dari objek yang mereka buat dan gunakan, meskipun tentu saja tidak ada cara menghindarinya kadang-kadang - selalu menguntungkan jika setidaknya memiliki gambaran kasar tentang cara kerja pengumpulan sampah .

Catatan sejarah: revisi jawaban sebelumnya memiliki referensi yang salah kepada deleteoperator. Dalam JavaScript , deleteoperator menghapus properti dari objek , dan sepenuhnya berbeda dengan deletedi C / C ++.

Noldorin
sumber
27
panduan Apple cacat: autor digunakan secara deletetidak benar; misalnya dalam contoh pertama, alih-alih delete foo, Anda harus terlebih dahulu menghapus event listener via window.removeEventListener()dan kemudian gunakan foo = nulluntuk menimpa variabel; di IE, delete window.foo(tetapi tidak delete foo) juga akan bekerja jika fooitu global, tetapi meskipun demikian itu tidak akan di FF atau Opera
Christoph
3
Ketahuilah bahwa artikel Eric harus dianggap "hanya untuk tujuan historis". Tapi ini masih informatif.
Peter Ivan
2
Perhatikan juga - IE 6 dan 7 JANGAN menggunakan kolektor sampah tanda dan sapuan non-generasi. Mereka menggunakan pengumpul penghitungan sampah referensi sederhana, yang lebih rentan terhadap masalah referensi melingkar dengan pengumpulan sampah.
Doug
1
ECMAScript deleteadalah operator unary (ekspresi), bukan pernyataan (yaitu:) delete 0, delete 0, delete 3. Itu terlihat seperti pernyataan ketika diekspresikan oleh pernyataan ekspresi.
Hydroper
Ya jawabannya pada saat ini sudah ketinggalan zaman, pada 2012, browser modern menggunakan tanda / sapuan algorthm .. jadi itu tidak tergantung ruang lingkup lagi. Referensi: developer.mozilla.org/en-US/docs/Web/JavaScript/…
sksallaj
52

Waspadai referensi melingkar ketika objek DOM terlibat:

Pola kebocoran memori dalam JavaScript

Ingatlah bahwa memori hanya dapat direklamasi ketika tidak ada referensi aktif ke objek. Ini adalah perangkap umum dengan penutupan dan penangan acara, karena beberapa mesin JS tidak akan memeriksa variabel mana yang sebenarnya direferensikan dalam fungsi dalam dan hanya menyimpan semua variabel lokal dari fungsi melampirkan.

Berikut ini contoh sederhana:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

Implementasi JS yang naif tidak dapat mengumpulkan bigStringselama event handler ada. Ada beberapa cara untuk mengatasi masalah ini, misalnya pengaturan bigString = nulldi akhir init()( deletetidak akan berfungsi untuk variabel lokal dan argumen fungsi: deletemenghapus properti dari objek, dan objek variabel tidak dapat diakses - ES5 dalam mode ketat bahkan akan melempar ReferenceErrorjika Anda mencoba untuk menghapus variabel lokal!).

Saya sarankan untuk menghindari penutupan yang tidak perlu sebanyak mungkin jika Anda peduli untuk konsumsi memori.

Christoph
sumber
20
Bug DOM bundar referensi khusus untuk JScript - tidak ada browser lain yang menderita kecuali IE. Sebenarnya saya cukup yakin bahwa ECMAScript spec secara eksplisit menyatakan bahwa GC harus dapat menangani siklus seperti: - /
olliej
@olliej: Saya tidak melihat penyebutan GC dalam spesifikasi ECMAScript .
Janus Troelsen
16

Kutipan yang bagus diambil dari blog

Komponen DOM adalah "sampah yang dikumpulkan", seperti komponen JScript, yang berarti bahwa jika Anda membuat objek di dalam salah satu komponen, dan kemudian kehilangan jejak objek itu, pada akhirnya akan dibersihkan.

Sebagai contoh:

function makeABigObject() {
var bigArray = new Array(20000);
}

Ketika Anda memanggil fungsi itu, komponen JScript membuat objek (bernama bigArray) yang dapat diakses di dalam fungsi. Segera setelah fungsi kembali, Anda "kehilangan jejak" bigArray karena tidak ada cara untuk merujuknya lagi. Nah, komponen JScript menyadari bahwa Anda telah kehilangan jejaknya, dan bigArray dibersihkan - memorinya diambil kembali. Hal yang sama berfungsi di komponen DOM. Jika Anda mengatakan document.createElement('div'), atau sesuatu yang serupa, maka komponen DOM membuat objek untuk Anda. Setelah Anda kehilangan jejak objek itu, komponen DOM akan membersihkan yang terkait.

TStamper
sumber
13

Sepengetahuan saya, objek JavaScript adalah sampah yang dikumpulkan secara berkala ketika tidak ada referensi yang tersisa untuk objek tersebut. Ini adalah sesuatu yang terjadi secara otomatis, tetapi jika Anda ingin melihat lebih lanjut tentang cara kerjanya, pada tingkat C ++, masuk akal untuk melihat kode sumber WebKit atau V8

Biasanya Anda tidak perlu memikirkannya, di browser yang lebih lama, seperti IE 5.5 dan versi awal IE 6, dan mungkin versi saat ini, penutupan akan membuat referensi melingkar bahwa ketika tidak dicentang akan berakhir memakan memori. Dalam kasus tertentu yang saya maksud tentang penutupan, saat Anda menambahkan referensi JavaScript ke objek dom, dan sebuah objek ke objek DOM yang merujuk kembali ke objek JavaScript. Pada dasarnya itu tidak pernah bisa dikumpulkan, dan pada akhirnya akan menyebabkan OS menjadi tidak stabil di aplikasi uji yang di-loop untuk membuat crash. Dalam praktiknya, kebocoran ini biasanya kecil, tetapi untuk menjaga kode Anda tetap bersih, Anda harus menghapus referensi JavaScript ke objek DOM.

Biasanya adalah ide yang baik untuk menggunakan kata kunci delete untuk segera membatalkan referensi objek besar seperti data JSON yang telah Anda terima kembali dan melakukan apa pun yang perlu Anda lakukan dengannya, terutama dalam pengembangan web seluler. Ini menyebabkan sapuan GC berikutnya untuk menghapus objek itu dan membebaskan memorinya.

Pelit Panas
sumber
Apakah JavaScript -> DOM -> masalah referensi melingkar JavaScript terpecahkan di versi IE yang lebih baru? Jika demikian, sejak kapan? Saya pikir arsitekturnya sangat dalam dan tidak mungkin diperbaiki. Apakah Anda punya sumber?
erikkallen
Hanya secara anekdot. Saya belum melihat kebocoran gila di IE 8 berjalan dalam mode standar, bukan mode rusak. Saya akan menyesuaikan respons saya.
Heat Miser
1
@ erikkallen: ya, bug GC telah diperbaiki di IE versi 8+, karena yang lebih tua menggunakan algoritma pengumpulan sampah yang sangat naif, yang membuatnya tidak mungkin untuk GC sepasang objek yang merujuk satu sama lain. mark-and-sweepAlgoritma gaya yang lebih baru menangani hal ini .
kumarharsh
6

pengumpulan sampah (GC) adalah bentuk manajemen memori otomatis dengan menghapus objek yang tidak diperlukan lagi.

setiap proses berurusan dengan memori ikuti langkah-langkah ini:

1 - mengalokasikan ruang memori yang Anda butuhkan

2 - melakukan pemrosesan

3 - kosongkan ruang memori ini

ada dua algoritma utama yang digunakan untuk mendeteksi objek mana yang tidak diperlukan lagi.

Pengumpulan sampah penghitungan referensi : algoritma ini mengurangi definisi "suatu objek tidak diperlukan lagi" menjadi "suatu objek tidak memiliki objek lain yang merujuk padanya", objek akan dihapus jika tidak ada titik referensi untuk itu

Algoritma mark-and-sweep : menghubungkan setiap objek ke sumber root. objek apa pun tidak terhubung ke root atau objek lain. objek ini akan dihapus.

Saat ini sebagian besar browser modern menggunakan algoritma kedua.

Ahmed Gaber - Biga
sumber
1
Dan untuk menambahkan sumber ini, lihat MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/…
Xenos
4

"Dalam ilmu komputer, pengumpulan sampah (GC) adalah bentuk manajemen memori otomatis. Pengumpul sampah, atau hanya pengumpul, berupaya untuk mendapatkan kembali sampah, atau memori yang digunakan oleh objek yang tidak akan pernah diakses atau dimutasi lagi oleh aplikasi."

Semua mesin JavaScript memiliki pemulung sendiri, dan mereka mungkin berbeda. Sebagian besar waktu Anda tidak harus berurusan dengan mereka karena mereka hanya melakukan apa yang seharusnya mereka lakukan.

Menulis kode yang lebih baik sebagian besar tergantung pada seberapa baik Anda tahu prinsip-prinsip pemrograman, bahasa dan implementasi tertentu.

mtasic85
sumber
1

Apa itu pengumpulan sampah JavaScript?

periksa ini

Apa yang penting bagi seorang programmer web untuk memahami tentang pengumpulan sampah JavaScript, untuk menulis kode yang lebih baik?

Dalam Javascript Anda tidak peduli tentang alokasi dan deallokasi memori. Seluruh masalah ditanyakan ke penerjemah Javascript. Kebocoran masih dimungkinkan dalam Javascript, tetapi mereka adalah bug dari penerjemah. Jika Anda tertarik dengan topik ini, Anda dapat membaca lebih lanjut di www.memorymanagement.org

dfa
sumber
Manakah dari berbagai sistem manajemen memori dalam artikel yang Anda tautkan yang digunakan oleh JavaScript? "Kebocoran masih dimungkinkan dalam Javascript, tetapi itu adalah bug dari penerjemah." - Itu tidak berarti pemrogram JS dapat mengabaikan seluruh masalah, misalnya ada JS yang cukup terkenal <-> masalah referensi melingkar DOM dalam versi IE yang lebih lama yang dapat Anda gunakan di dalam kode JS Anda. Juga, cara penutupan JS berfungsi adalah fitur desain, bukan bug, tetapi Anda dapat mengikat memori yang lebih besar daripada yang dimaksudkan jika Anda menggunakan penutupan "secara tidak tepat" (Saya tidak mengatakan jangan gunakan mereka).
nnnnnn
3
Kebocoran memori adalah binatang dalam JavaScript. Jika Anda sedang menulis aplikasi "proyek kuliah" sederhana, maka jangan khawatir. Tetapi ketika Anda mulai menulis aplikasi tingkat perusahaan berkinerja tinggi, manajemen memori dalam JavaScript adalah suatu keharusan.
Doug
1

Pada windows Anda dapat menggunakan Drip.exe untuk menemukan kebocoran memori atau memeriksa apakah mem bebas Anda berfungsi.

Ini sangat sederhana, cukup masukkan URL situs web dan Anda akan melihat konsumsi memori dari renderer IE terintegrasi. Kemudian tekan refresh, jika memori meningkat, Anda menemukan kebocoran memori di suatu tempat di halaman web. Tetapi ini juga sangat berguna untuk melihat apakah rutinitas untuk membebaskan memori berfungsi untuk IE.

powtac
sumber
1

Tipe referensi tidak menyimpan objek secara langsung ke dalam variabel yang ditugaskan, jadi variabel objek dalam contoh ini sebenarnya tidak mengandung instance objek. Sebagai gantinya, ia memegang pointer (atau referensi) ke lokasi di memori tempat objek itu ada

var object = new Object();

jika Anda menetapkan satu variabel ke variabel lain, masing-masing variabel mendapatkan salinan pointer, dan keduanya masih merujuk objek yang sama dalam memori.

var object1 = new Object();
var object2 = object1;

Dua variabel menunjuk ke satu objek

JavaScript adalah bahasa yang dikumpulkan dari sampah , jadi Anda tidak perlu khawatir tentang alokasi memori saat menggunakan jenis referensi. Namun, sebaiknya lakukan dereferensi objek yang tidak lagi Anda perlukan sehingga pengumpul sampah dapat membebaskan memori itu. Cara terbaik untuk melakukan ini adalah dengan mengatur variabel objek ke nol.

var object1 = new Object();
// do something
object1 = null; // dereference

Objek dereferencing sangat penting dalam aplikasi yang sangat besar yang menggunakan jutaan objek.

dari Prinsip JavaScript Berorientasi Objek - NICHOLAS C. ZAKAS

Ani Naslyan
sumber