1. Mekanisme pengumpulan sampah—GC
Javascript mempunyai mekanisme pengumpulan sampah automatik (GC: Pengumpulan Sampah), yang bermaksud bahawa persekitaran pelaksanaan bertanggungjawab untuk mengurus memori yang digunakan semasa pelaksanaan kod.
Prinsip: Pengumpul sampah akan secara berkala (secara berkala) mencari pembolehubah yang tidak lagi digunakan, dan kemudian melepaskan ingatannya.
Mekanisme pengumpulan sampah JavaScript adalah sangat mudah: cari pembolehubah yang tidak lagi digunakan, dan kemudian lepaskan memori yang didudukinya Namun, proses ini bukan masa nyata kerana overhednya agak besar, jadi pengumpul sampah akan mengikut jadual tetap Laksanakan secara berkala pada selang masa tertentu .
Pembolehubah yang tidak digunakan lagi ialah pembolehubah yang kitaran hayatnya telah tamat Sudah tentu, ia hanya boleh menjadi pembolehubah tempatan Kitaran hayat pembolehubah global tidak akan tamat sehingga pelayar memunggah halaman. Pembolehubah tempatan hanya wujud semasa pelaksanaan fungsi, dan semasa proses ini, ruang yang sepadan akan diperuntukkan untuk pembolehubah tempatan pada tindanan atau timbunan untuk menyimpan nilainya, dan kemudian pembolehubah ini akan digunakan dalam fungsi sehingga akhir fungsi , dan penutupan Disebabkan fungsi dalaman dalam pakej, fungsi luaran tidak boleh dianggap sebagai penamat.
Mari terangkan kod:
function fn1() { var obj = {name: 'hanzichi', age: 10}; } function fn2() { var obj = {name:'hanzichi', age: 10}; return obj; } var a = fn1(); var b = fn2();
Mari lihat cara kod dilaksanakan. Pertama, dua fungsi ditakrifkan, masing-masing dipanggil fn1 dan fn2 Apabila fn1 dipanggil, memasuki persekitaran fn1, memori akan dibuka untuk menyimpan objek {name: 'hanzichi', umur: 10}, dan apabila panggilan itu. selesai, persekitaran fn1, maka blok memori ini akan dikeluarkan secara automatik oleh pengumpul sampah dalam enjin js semasa proses fn2 dipanggil, objek yang dikembalikan ditunjuk oleh pembolehubah global b, jadi blok memori ini akan; tidak dilepaskan.
Persoalan timbul di sini: Pembolehubah manakah yang tidak berguna? Oleh itu, pemungut sampah mesti menjejaki pembolehubah mana yang tidak berguna dan menandakan pembolehubah yang tidak lagi berguna untuk persediaan untuk menuntut semula ingatan mereka pada masa hadapan. Strategi yang digunakan untuk menanda pembolehubah yang tidak digunakan mungkin berbeza dari pelaksanaan ke pelaksanaan, tetapi secara amnya terdapat dua pelaksanaan: penyapuan tanda dan pengiraan rujukan. Pengiraan rujukan kurang biasa digunakan, tanda-dan-sapu lebih biasa digunakan.
2. Tandakan pembersihan
Kaedah pengumpulan sampah yang paling biasa digunakan dalam js ialah pembersihan tanda. Apabila pembolehubah memasuki persekitaran, contohnya, dengan mengisytiharkan pembolehubah dalam fungsi, pembolehubah ditandakan sebagai "memasuki persekitaran." Secara logiknya, memori yang diduduki oleh pembolehubah yang memasuki persekitaran tidak boleh dilepaskan, kerana ia boleh digunakan apabila aliran pelaksanaan memasuki persekitaran yang sepadan. Dan apabila pembolehubah meninggalkan persekitaran, ia ditandakan sebagai "meninggalkan persekitaran".
function test(){ var a = 10 ; //被标记 ,进入环境 var b = 20 ; //被标记 ,进入环境 } test(); //执行完毕 之后 a、b又被标离开环境,被回收。
Apabila pemungut sampah berjalan, ia akan menandakan semua pembolehubah yang disimpan dalam ingatan (sudah tentu, sebarang kaedah penandaan boleh digunakan). Kemudian, ia mengalih keluar teg (penutupan) pembolehubah dalam persekitaran dan pembolehubah yang dirujuk oleh pembolehubah dalam persekitaran. Pembolehubah yang ditandakan selepas ini akan dianggap sebagai pembolehubah yang akan dipadamkan kerana pembolehubah dalam persekitaran tidak lagi boleh mengakses pembolehubah ini. Akhirnya, pemungut sampah menyelesaikan kerja pembersihan memori, memusnahkan nilai bertanda tersebut dan menuntut semula ruang memori yang mereka duduki.
Setakat ini, pelaksanaan js IE, Firefox, Opera, Chrome dan Safari semuanya menggunakan strategi pengumpulan sampah tanda-dan-sapu atau strategi yang serupa, tetapi selang masa pengumpulan sampah adalah berbeza.
3. Kiraan Rujukan
Maksud pengiraan rujukan ialah menjejak bilangan kali setiap nilai dirujuk. Apabila pembolehubah diisytiharkan dan nilai jenis rujukan diberikan kepada pembolehubah, bilangan rujukan kepada nilai ini ialah 1. Jika nilai yang sama diberikan kepada pembolehubah lain, kiraan rujukan nilai itu dinaikkan sebanyak 1. Sebaliknya, jika pembolehubah yang mengandungi rujukan kepada nilai ini memperoleh nilai lain, bilangan rujukan kepada nilai ini dikurangkan dengan satu. Apabila bilangan rujukan kepada nilai ini menjadi 0, ini bermakna tiada cara untuk mengakses nilai ini lagi, jadi ruang memori yang didudukinya boleh dituntut semula. Dengan cara ini, apabila pemungut sampah berjalan pada masa akan datang, ia akan melepaskan memori yang diduduki oleh nilai tersebut dengan kiraan rujukan 0.
function test(){ var a = {} ; //a的引用次数为0 var b = a ; //a的引用次数加1,为1 var c =a; //a的引用次数再加1,为2 var b ={}; //a的引用次数减1,为1 }
Netscape Navigator3 ialah penyemak imbas pertama yang menggunakan strategi pengiraan rujukan, tetapi tidak lama kemudian ia menghadapi masalah serius: rujukan pekeliling. Rujukan bulat bermaksud objek A mengandungi penunjuk ke objek B, dan objek B juga mengandungi rujukan kepada objek A.
function fn() { var a = {}; var b = {}; a.pro = b; b.pro = a; } fn();
以上代码a和b的引用次数都是2,fn()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为a和b的引用次数不为0,所以不会被垃圾回收器回收内存,如果fn函数被大量调用,就会造成内存泄露。在IE7与IE8上,内存直线上升。
我们知道,IE中有一部分对象并不是原生js对象。例如,其内存泄露DOM和BOM中的对象就是使用C++以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数策略。因此,即使IE的js引擎采用标记清除策略来实现,但js访问的COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及COM对象,就会存在循环引用的问题。
var element = document.getElementById("some_element"); var myObject = new Object(); myObject.e = element; element.o = myObject;
这个例子在一个DOM元素(element)与一个原生js对象(myObject)之间创建了循环引用。其中,变量myObject有一个名为element的属性指向element对象;而变量element也有一个属性名为o回指myObject。由于存在这个循环引用,即使例子中的DOM从页面中移除,它也永远不会被回收。
看上面的例子,有同学回觉得太弱了,谁会做这样无聊的事情,其实我们是不是就在做
window.onload=function outerFunction(){ var obj = document.getElementById("element"); obj.onclick=function innerFunction(){}; };
这段代码看起来没什么问题,但是obj引用了document.getElementById(“element”),而document.getElementById(“element”)的onclick方法会引用外部环境中德变量,自然也包括obj,是不是很隐蔽啊。
解决办法
最简单的方式就是自己手工解除循环引用,比如刚才的函数可以这样
myObject.element = null; element.o = null;
window.onload=function outerFunction(){ var obj = document.getElementById("element"); obj.onclick=function innerFunction(){}; obj=null; };
Menetapkan pembolehubah kepada null bermakna memutuskan sambungan antara pembolehubah dan nilai yang dirujuk sebelum ini. Pada kali berikutnya pemungut sampah berjalan, nilai-nilai ini dialih keluar dan memori yang mereka duduki akan dituntut semula.
Perlu diambil perhatian bahawa IE9 tidak mengalami masalah kebocoran memori Dom yang disebabkan oleh rujukan bulat. Mungkin Microsoft telah membuat pengoptimuman atau cara Dom dikitar semula telah berubah
4. Pengurusan Memori
1. Bilakah kutipan sampah dicetuskan?
Pemungut sampah berjalan secara berkala Jika banyak memori diperuntukkan, kerja kitar semula akan menjadi sangat sukar untuk menentukan selang waktu pengumpulan sampah menjadi persoalan yang patut difikirkan. Pengumpulan sampah IE6 berjalan berdasarkan jumlah peruntukan memori Apabila terdapat 256 pembolehubah, 4096 objek atau 64k rentetan dalam persekitaran, pengumpul sampah akan dicetuskan, ia kelihatan sangat saintifik, dan tidak perlu menekan satu perenggan. Ia hanya dipanggil sekali setiap kali, kadang-kadang tidak perlu, bukankah lebih baik untuk memanggilnya atas permintaan? Tetapi jika terdapat begitu banyak pembolehubah dalam persekitaran dan skripnya begitu kompleks sekarang, ia adalah perkara biasa, maka hasilnya ialah pengumpul sampah sentiasa berfungsi, jadi pelayar tidak boleh bermain.
Microsoft telah membuat pelarasan dalam IE7 Keadaan pencetus tidak lagi tetap, tetapi diubah suai secara dinamik adalah sama dengan IE6 Jika jumlah peruntukan memori yang dipulihkan oleh pengumpul sampah adalah kurang daripada 15% daripada memori yang diduduki. oleh program, ia bermakna Kebanyakan memori tidak boleh dikitar semula Keadaan pencetus yang ditetapkan untuk pengumpulan sampah Pada masa ini, dua kali ganda keadaan jalan Jika memori yang dikitar semula adalah lebih tinggi daripada 85%, ini bermakna kebanyakannya memori harus dibersihkan lama dahulu. Pada masa ini, tetapkan keadaan pencetus. Ini menjadikan fungsi pengumpulan sampah lebih mudah
2. Pelan GC yang munasabah
1) Penyelesaian asas GC enjin Javascript ialah (GC ringkas): tanda dan sapu, iaitu:
2), kecacatan GC
Seperti bahasa lain, strategi GC JavaScript tidak dapat mengelakkan masalah: semasa GC, ia berhenti bertindak balas kepada operasi lain Ini adalah atas sebab keselamatan. GC Javascript adalah 100ms atau lebih, yang sesuai untuk aplikasi umum, tetapi untuk permainan JS dan aplikasi animasi yang memerlukan kesinambungan yang agak tinggi, ia menyusahkan. Inilah yang perlu dioptimumkan oleh enjin baharu: untuk mengelakkan jeda panjang sebagai tindak balas yang disebabkan oleh GC.
3), strategi pengoptimuman GC
Pakcik David terutamanya memperkenalkan 2 rancangan pengoptimuman, dan ini juga merupakan dua rancangan pengoptimuman yang paling penting:
(1) Generasi GC (Generasi GC)
Ini selaras dengan idea strategi kitar semula Java. Tujuannya untuk membezakan antara objek "sementara" dan "persistent"; , dengan itu mengurangkan setiap GC yang memakan masa. Seperti yang ditunjukkan dalam gambar:
Apa yang perlu ditambah di sini ialah: untuk objek generasi tenured, terdapat overhed tambahan: memindahkannya daripada generasi muda kepada generasi tenured Selain itu, jika dirujuk, titik rujukan juga perlu diubah suai.
(2) GC Bertambah
Idea penyelesaian ini sangat mudah, iaitu, "proses sedikit setiap kali, dan kemudian proses sedikit masa seterusnya, dan seterusnya." Seperti yang ditunjukkan dalam gambar:
Walaupun penyelesaian ini mengambil masa yang singkat, ia mempunyai banyak gangguan dan membawa masalah penukaran konteks yang kerap.
Oleh kerana setiap penyelesaian mempunyai senario dan kekurangan yang sesuai, dalam aplikasi sebenar, penyelesaian akan dipilih berdasarkan situasi sebenar.
Contohnya: apabila nisbah (objek/s) rendah, kekerapan perlaksanaan gangguan GC adalah lebih rendah, dan GC mudah adalah lebih rendah jika sebilangan besar objek "bertahan" untuk masa yang lama, kelebihan generasi pemprosesan tidak bagus.
Rujukan:
Perkara di atas adalah mengenai mekanisme pengumpulan sampah dan pengurusan ingatan JavaScript. Saya harap ia akan membantu pembelajaran semua orang.