Pada artikel ini, saya akan memperkenalkan Anda tentang cara kerja alokasi memori dan pengumpulan sampah dan bagaimana Anda dapat menghindari beberapa kebocoran memori yang umum
Bagaimana cara kerja JavaScript di browser?
Artikel ini adalah bagian kedua dari seri posting saya, di mana saya menjelaskan cara kerja JavaScript di browser. Untuk mendapatkan artikel terbaru saya ke kotak masuk Anda, berlangganan buletin saya
- Bagian 1. JavaScript Event Loop Dan Call Stack Dijelaskan
- Bagian 2. Manajemen Memori JavaScript. Penjelasan Tumpukan Dan Pengumpulan Sampah
Siklus hidup memori
Dalam JavaScript, saat kita membuat variabel, fungsi, atau apa pun yang dapat Anda pikirkan, mesin JS mengalokasikan memori untuk ini dan melepaskannya setelah tidak diperlukan lagi
Mengalokasikan memori adalah proses memesan ruang dalam memori, sementara melepaskan memori membebaskan ruang, siap digunakan untuk tujuan lain
Setiap kali kita menetapkan variabel atau membuat fungsi, memori untuk itu selalu melewati tahapan yang sama
Alokasikan memori
JavaScript menangani ini untuk kami. Itu mengalokasikan memori yang kita perlukan untuk objek yang kita buat
Gunakan memori
Menggunakan memori adalah sesuatu yang kami lakukan secara eksplisit dalam kode kami. Membaca dan menulis ke memori tidak lain adalah membaca atau menulis dari atau ke variabel
Lepaskan memori
Langkah ini juga ditangani oleh mesin JavaScript. Setelah memori yang dialokasikan dilepaskan, itu dapat digunakan untuk tujuan baru
"Objek" dalam konteks manajemen memori tidak hanya mencakup objek JS tetapi juga fungsi dan cakupan fungsi
Tumpukan dan tumpukan memori
Kami sekarang tahu bahwa untuk semua yang kami definisikan dalam JavaScript, mesin mengalokasikan memori dan membebaskannya setelah kami tidak membutuhkannya lagi
Pertanyaan berikutnya yang muncul di benak saya adalah. Di mana ini akan disimpan?
Mesin JavaScript memiliki dua tempat untuk menyimpan data. Tumpukan dan tumpukan memori
Tumpukan dan tumpukan adalah dua struktur data yang digunakan mesin untuk tujuan berbeda
Tumpukan. Alokasi memori statis
Anda mungkin tahu tumpukan dari bagian pertama seri ini pada tumpukan panggilan dan loop peristiwa, di mana saya berfokus pada bagaimana itu digunakan untuk melacak fungsi yang perlu dipanggil oleh juru bahasa JS
Semua nilai disimpan dalam tumpukan karena semuanya berisi nilai primitif
Tumpukan adalah struktur data yang digunakan JavaScript untuk menyimpan data statis. Data statis adalah data di mana mesin mengetahui ukurannya pada waktu kompilasi. Dalam JavaScript, ini termasuk nilai-nilai primitif (string, angka, boolean, undefined, dan null) dan referensi, yang menunjuk ke objek dan fungsi
Karena mesin mengetahui bahwa ukurannya tidak akan berubah, mesin akan mengalokasikan jumlah memori yang tetap untuk setiap nilai
Proses pengalokasian memori tepat sebelum eksekusi dikenal sebagai alokasi memori statis
Karena mesin mengalokasikan jumlah memori yang tetap untuk nilai-nilai ini, ada batasan seberapa besar nilai-nilai primitif
Batas nilai ini dan seluruh tumpukan bervariasi tergantung pada browser
Tumpukan. Alokasi memori dinamis
Tumpukan adalah ruang berbeda untuk menyimpan data tempat JavaScript menyimpan objek dan fungsi
Berbeda dengan tumpukan, mesin tidak mengalokasikan jumlah memori tetap untuk objek ini. Sebaliknya, lebih banyak ruang akan dialokasikan sesuai kebutuhan
Mengalokasikan memori dengan cara ini juga disebut alokasi memori dinamis
Untuk mendapatkan gambaran umum, berikut adalah fitur dari kedua penyimpanan yang dibandingkan secara berdampingan
StackHeapNilai dan referensi primitifObjek dan fungsiUkuran diketahui pada waktu kompilasiUkuran diketahui pada waktu prosesMengalokasikan jumlah memori tetapTidak ada batasan per objekContoh
Mari kita lihat beberapa contoh kode. Dalam keterangan saya menyebutkan apa yang dialokasikan
const person = { name: 'John', age: 24, };const hobbies = ['hiking', 'reading'];Array adalah objek juga, itulah sebabnya mereka disimpan di heap
let name = 'John'; // allocates memory for a string const age = 24; // allocates memory for a number name = 'John Doe'; // allocates memory for a new string const firstName = name.slice(0,4); // allocates memory for a new stringNilai primitif tidak dapat diubah, yang berarti alih-alih mengubah nilai asli, JavaScript membuat yang baru
Referensi dalam JavaScript
Semua variabel pertama menunjuk ke tumpukan. Jika itu adalah nilai non-primitif, tumpukan berisi referensi ke objek di tumpukan
Memori heap tidak diurutkan dengan cara tertentu, oleh karena itu kita perlu menyimpan referensi ke dalam stack. Anda dapat menganggap referensi sebagai alamat dan objek di heap sebagai rumah tempat alamat tersebut berada
Ingatlah bahwa JavaScript menyimpan objek dan fungsi di helai. Nilai dan referensi primitif disimpan dalam tumpukan
Dalam gambar ini, kita dapat mengamati bagaimana berbagai nilai disimpan. Perhatikan bagaimana const hobbies = ['hiking', 'reading'];_2 dan const hobbies = ['hiking', 'reading'];3 keduanya menunjuk ke objek yang sama
Contoh
const person = { name: 'John', age: 24, };Ini membuat objek baru di tumpukan dan referensi ke dalam tumpukan
Referensi adalah konsep inti tentang cara kerja JavaScript. Membahas lebih detail di sini akan berada di luar cakupan artikel ini, tetapi jika Anda ingin mempelajarinya lebih lanjut, beri tahu saya di komentar dan berlangganan buletin saya
Pengumpulan sampah
Kami sekarang tahu bagaimana JavaScript mengalokasikan memori untuk semua jenis objek, tetapi jika kami mengingat siklus hidup memori, ada satu langkah terakhir yang hilang. melepaskan ingatan
Sama seperti alokasi memori, mesin JavaScript juga menangani langkah ini untuk kita. Lebih khusus lagi, pengumpul sampah menangani ini
Setelah mesin JavaScript mengenali bahwa variabel atau fungsi yang diberikan tidak diperlukan lagi, ia melepaskan memori yang ditempati
Masalah utama dengan ini adalah apakah beberapa memori masih diperlukan atau tidak adalah masalah yang tidak dapat diputuskan, yang berarti tidak mungkin ada algoritme yang dapat mengumpulkan semua memori yang tidak diperlukan lagi pada saat yang tepat menjadi usang.
Beberapa algoritma menawarkan pendekatan yang baik untuk masalah tersebut. Saya akan membahas yang paling banyak digunakan di bagian ini. Pengumpulan sampah penghitungan referensi dan algoritme mark and sweep
Pengumpulan sampah yang menghitung referensi
Yang ini adalah perkiraan termudah. Itu mengumpulkan objek yang tidak memiliki referensi yang menunjuk ke sana
Mari kita lihat contoh berikut. Garis mewakili referensi
Perhatikan bagaimana di frame terakhir hanya const hobbies = ['hiking', 'reading'];_4 yang tetap berada di heap karena itu adalah objek yang memiliki referensi pada akhirnya
Siklus
Masalah dengan algoritme ini adalah tidak mempertimbangkan referensi siklik. Ini terjadi ketika satu atau lebih objek saling mereferensikan, tetapi mereka tidak dapat diakses melalui kode lagi
Karena objek const hobbies = ['hiking', 'reading'];5 dan const hobbies = ['hiking', 'reading'];6 saling merujuk satu sama lain, algoritme tidak akan melepaskan memori yang dialokasikan. Tidak ada cara bagi kami untuk mengakses kedua objek itu lagi
Menyetelnya ke const hobbies = ['hiking', 'reading'];_7 tidak akan membuat algoritme penghitungan referensi menyadari bahwa keduanya tidak dapat digunakan lagi karena keduanya memiliki referensi yang masuk
Algoritma Mark-and-sweep
Algoritme mark-and-sweep memiliki solusi untuk dependensi siklik. Alih-alih hanya menghitung referensi ke objek tertentu, ia mendeteksi jika dapat dijangkau dari objek root
Akar di browser adalah objek const hobbies = ['hiking', 'reading'];_8, sedangkan di NodeJS ini adalah const hobbies = ['hiking', 'reading'];9
Algoritme menandai objek yang tidak dapat dijangkau sebagai sampah, dan menyapu (mengumpulkan) setelahnya. Objek root tidak akan pernah dikumpulkan
Dengan cara ini, dependensi siklik tidak menjadi masalah lagi. Dalam contoh sebelumnya, baik objek const hobbies = ['hiking', 'reading'];_6 maupun const hobbies = ['hiking', 'reading'];5 tidak dapat dijangkau dari root. Dengan demikian, keduanya akan ditandai sebagai sampah dan dikumpulkan
Sejak 2012, algoritme ini diterapkan di semua browser modern. Perbaikan hanya dilakukan pada kinerja dan implementasi, tetapi tidak pada ide inti algoritme itu sendiri
Trade-off
Pengumpulan sampah otomatis memungkinkan kita untuk fokus membangun aplikasi daripada kehilangan waktu dengan manajemen memori. Namun, ada beberapa pengorbanan yang perlu kita waspadai
penggunaan memori
Mengingat algoritme tidak dapat mengetahui kapan tepatnya memori tidak diperlukan lagi, aplikasi JavaScript dapat menggunakan lebih banyak memori daripada yang sebenarnya dibutuhkan
Meskipun objek ditandai sebagai sampah, terserah pengumpul sampah untuk memutuskan kapan dan apakah memori yang dialokasikan akan dikumpulkan
Jika Anda membutuhkan aplikasi Anda seefisien mungkin memori, Anda lebih baik menggunakan bahasa tingkat rendah. Namun perlu diingat bahwa ini datang dengan serangkaian pengorbanannya sendiri
Pertunjukan
Algoritme yang mengumpulkan sampah untuk kita biasanya berjalan secara berkala untuk membersihkan objek yang tidak terpakai
Masalahnya adalah kami, para pengembang, tidak tahu kapan tepatnya ini akan terjadi. Mengumpulkan banyak sampah atau sering mengumpulkan sampah dapat memengaruhi kinerja karena memerlukan sejumlah daya komputasi untuk melakukannya
Namun, dampaknya biasanya tidak terlihat oleh pengguna atau pengembang
Memori bocor
Berbekal semua pengetahuan tentang manajemen memori ini, mari kita lihat kebocoran memori yang paling umum
Anda akan melihat bahwa ini dapat dengan mudah dihindari jika seseorang memahami apa yang terjadi di balik layar
Variabel global
Menyimpan data dalam variabel global mungkin merupakan jenis kebocoran memori yang paling umum
Dalam JavaScript untuk browser, jika Anda mengabaikan let name = 'John'; // allocates memory for a string const age = 24; // allocates memory for a number name = 'John Doe'; // allocates memory for a new string const firstName = name.slice(0,4); // allocates memory for a new string2, let name = 'John'; // allocates memory for a string const age = 24; // allocates memory for a number name = 'John Doe'; // allocates memory for a new string const firstName = name.slice(0,4); // allocates memory for a new string3, atau let name = 'John'; // allocates memory for a string const age = 24; // allocates memory for a number name = 'John Doe'; // allocates memory for a new string const firstName = name.slice(0,4); // allocates memory for a new string4, variabel akan dilampirkan ke objek const hobbies = ['hiking', 'reading'];8
users = getUsers();Hindari ini dengan menjalankan kode Anda dalam mode ketat
Selain menambahkan variabel secara tidak sengaja ke root, ada banyak kasus di mana Anda mungkin melakukannya dengan sengaja
Anda pasti dapat menggunakan variabel global, tetapi pastikan Anda mengosongkan ruang setelah Anda tidak memerlukan data lagi
Untuk melepaskan memori, tetapkan variabel global ke const hobbies = ['hiking', 'reading'];7
Saya ingin membuat artikel ini semudah mungkin untuk dipahami. Jika Anda memiliki pertanyaan terbuka, kirimkan saya email atau tinggalkan komentar. Saya akan mencoba membantu Anda dan meningkatkan artikel dengan umpan balik Anda
Penghitung waktu dan panggilan balik yang terlupakan
Melupakan pengatur waktu dan panggilan balik dapat membuat penggunaan memori aplikasi Anda meningkat. Khususnya di Single Page Applications (SPA), Anda harus berhati-hati saat menambahkan event listener dan callback secara dinamis
Timer yang terlupakan
const object = {}; const intervalId = setInterval(function() { // everything used in here can't be collected // until the interval is cleared doSomething(object); }, 2000);Kode di atas menjalankan fungsi setiap 2 detik. Jika Anda memiliki kode seperti ini di proyek Anda, Anda mungkin tidak memerlukan ini untuk dijalankan sepanjang waktu
Objek yang direferensikan dalam interval tidak akan menjadi sampah yang dikumpulkan selama interval tidak dibatalkan
Pastikan untuk menghapus interval setelah tidak diperlukan lagi
clearInterval(intervalId);Ini sangat penting dalam SPA. Bahkan saat keluar dari halaman yang memerlukan interval ini, interval ini akan tetap berjalan di latar belakang
Panggilan balik yang terlupakan
Katakanlah Anda menambahkan pendengar let name = 'John'; // allocates memory for a string const age = 24; // allocates memory for a number name = 'John Doe'; // allocates memory for a new string const firstName = name.slice(0,4); // allocates memory for a new string_7 ke sebuah tombol, yang nantinya akan dihapus
Browser lama tidak dapat mengumpulkan pendengar, tetapi saat ini, ini bukan masalah lagi
Tetap saja, sebaiknya hapus pendengar acara setelah Anda tidak membutuhkannya lagi
const element = document.getElementById('button'); const onClick = () => alert('hi'); element.addEventListener('click', onClick); element.removeEventListener('click', onClick); element.parentNode.removeChild(element);Referensi DOM habis
Kebocoran memori ini mirip dengan yang sebelumnya. Itu terjadi saat menyimpan elemen DOM dalam JavaScript
const hobbies = ['hiking', 'reading'];_0Saat Anda menghapus salah satu elemen tersebut, Anda mungkin ingin memastikan untuk menghapus elemen ini dari array juga
Jika tidak, elemen DOM ini tidak dapat dikumpulkan
const hobbies = ['hiking', 'reading'];_1Menghapus elemen dari array membuatnya tetap sinkron dengan DOM
Karena setiap elemen DOM juga menyimpan referensi ke node induknya, Anda akan mencegah pengumpul sampah mengumpulkan induk dan turunan elemen tersebut
Kesimpulan
Pada artikel ini, saya merangkum konsep inti manajemen memori dalam JavaScript
Menulis artikel ini membantu saya menjernihkan beberapa konsep yang tidak saya pahami sepenuhnya, dan saya harap ini akan berfungsi sebagai ikhtisar yang baik tentang cara kerja manajemen memori dalam JavaScript
Putar Ulang Sesi Sumber Terbuka
OpenReplay adalah rangkaian pemutaran ulang sesi sumber terbuka yang memungkinkan Anda melihat apa yang dilakukan pengguna di aplikasi web Anda, membantu Anda memecahkan masalah dengan lebih cepat. OpenReplay dihosting sendiri untuk kontrol penuh atas data Anda
Mulai nikmati pengalaman debug Anda - mulai gunakan OpenReplay secara gratis
Saya telah mempelajari ini dari beberapa artikel bagus lainnya yang ingin saya sebutkan di sini juga
- Manajemen Memori - Dokumen Web MDN
Sumber yang bagus untuk membaca tentang topik ini lagi
- Manajemen memori di V8
Artikel ini membahas lebih detail tentang cara kerja mesin V8. Saya menemukan ini sangat menarik
Artikel lain yang mungkin menarik bagi Anda
- JavaScript Event Loop Dan Call Stack Dijelaskan
Pada bagian pertama dari seri blog ini, saya menjelaskan mengapa Anda dapat melakukan banyak hal secara bersamaan di browser, meskipun JavaScript adalah bahasa single-threaded.
- 9 topik favorit saya tentang "The Pragmatic Programmer"
Membaca adalah cara yang bagus untuk meningkatkan keterampilan pemrograman Anda. Pada artikel ini, saya membagikan intisari dari buku pemrograman favorit saya
- JavaScript Heap Out Of Memory Kesalahan
Artikel ini terkait erat dengan artikel ini karena menjelaskan apa yang dapat Anda lakukan tentang Node.js Anda. aplikasi js kehabisan memori