javascript
深入浅出 JavaScript 内存管理,垃圾回收
簡(jiǎn)介
本篇文章講解JavaScript 中垃圾回收機(jī)制,內(nèi)存泄漏,結(jié)合一些常遇到的例子,相信各位看完后,會(huì)對(duì)JS 中垃圾回收機(jī)制有個(gè)深入的了解。
我的github,歡迎 star
內(nèi)存生命周期
首先,不管什么程序語(yǔ)言,內(nèi)存生命周期基本是一致的:
- 分配你所需要的內(nèi)存
- 使用分配到的內(nèi)存(讀、寫(xiě))
- 不需要時(shí)將其釋放歸還
?在所有語(yǔ)言中第一和第二部分都很清晰。最后一步在低級(jí)語(yǔ)言中(C語(yǔ)言等)很清晰,但是在像JavaScript 等高級(jí)語(yǔ)言中,這一步是隱藏的、透明的。因?yàn)镴avaScript 具有自動(dòng)垃圾收集機(jī)制(Garbage collected )。在編寫(xiě) JS 時(shí),不需要關(guān)心內(nèi)存使用問(wèn)題,所需內(nèi)存分配以及無(wú)用內(nèi)存的回收完全實(shí)現(xiàn)了自動(dòng)管理。
內(nèi)存泄漏
內(nèi)存泄漏(memory leaks),什么情況下回導(dǎo)致內(nèi)存泄漏?可以簡(jiǎn)單理解為有些代碼本來(lái)要被回收的,但沒(méi)有被回收,還一直占用著操作系統(tǒng)內(nèi)存,從而越積越多,最終會(huì)導(dǎo)致內(nèi)存泄漏(可以理解為,內(nèi)存滿了,就溢出了)。
管理內(nèi)存(Memory Management)
分配給web瀏覽器的可用內(nèi)存數(shù)量通常要比分配給桌面應(yīng)用程序少。這樣做的目的主要是處于安全方面考慮,目的是防止運(yùn)行JS 的網(wǎng)頁(yè)耗盡全部系統(tǒng)內(nèi)存而導(dǎo)致系統(tǒng)崩潰。內(nèi)存限制問(wèn)題不僅會(huì)影響給變量分配內(nèi)存,同時(shí)還會(huì)影響調(diào)用棧以及在一個(gè)線程中能夠同時(shí)執(zhí)行的語(yǔ)句數(shù)量。
因此,確保占用最少的內(nèi)存可以讓頁(yè)面獲得更好的性能。而優(yōu)化內(nèi)存占用的最佳方式,就是為執(zhí)行中的代碼只保存必要的數(shù)據(jù)。一旦數(shù)據(jù)不再有用,最好通過(guò)將其值設(shè)置為 null 來(lái)釋放其引用。這個(gè)方法叫做解除引用。這一做法適用于大多數(shù)的全局變量和全局對(duì)象的屬性。局部變量會(huì)在他們離開(kāi)執(zhí)行環(huán)境時(shí)自動(dòng)被解除引用。
解除一個(gè)值的引用并不意味著自動(dòng)回收改值所占用的內(nèi)存。解除引用的真正作用是讓值脫離執(zhí)行環(huán)境,以便垃圾收集器下次運(yùn)行時(shí)將其回收。
標(biāo)記清除(Mark and Sweep)
通常,垃圾收集器(garbage collector)在運(yùn)行時(shí)候會(huì)給儲(chǔ)存在內(nèi)存中的所有變量都加上標(biāo)記。然后,它會(huì)去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記。而在此之后再被加上標(biāo)記的變量將被視為準(zhǔn)備刪除的變量,原因是環(huán)境中的變量已經(jīng)無(wú)法訪問(wèn)到這些變量了。最后,垃圾收集器完成內(nèi)存清除的工作。
那標(biāo)記清除具體是如何呢?有以下幾種算法:
- 在JavaScript 中,全局變量(Global)和window 對(duì)象會(huì)一直存在,不會(huì)被垃圾收集器回收;
- 遞歸所用到的所有(包括變量和方法),都不會(huì)被回收;
- 所有沒(méi)有被標(biāo)記為“活躍(active)”的,都會(huì)被認(rèn)為是垃圾,收集器釋放會(huì)回收垃圾,并把內(nèi)存還給操作系統(tǒng)。
例子:
例一:
var n = 123; // 給數(shù)值變量分配內(nèi)存var s = "azerty"; // 給字符串分配內(nèi)存// 給對(duì)象及其包含的值分配內(nèi)存 var o = {a: 1,b: null };// 給函數(shù)(可調(diào)用的對(duì)象)分配內(nèi)存 function f(a){return a + 2; }例二:
function foo(arg) {// 此處bar 是全局變量,window.bar 可以訪問(wèn),所以也不會(huì)被回收bar = "this is a hidden global variable"; } function foo() {// 此處this 代表 windowthis.variable = "potential accidental global"; }例三:
var someResource = getData(); setInterval(function() {var node = document.getElementById('Node');if(node) {node.innerHTML = JSON.stringify(someResource));} }, 1000);// 上面這段代碼,定時(shí)器setInterval 和 someResource 一直存在,不會(huì)被回收。可以改成下面代碼var element = document.getElementById('button');function onClick(event) {element.innerHtml = 'text'; }element.addEventListener('click', onClick); // 手動(dòng)移除事件監(jiān)聽(tīng)器和變量 element.removeEventListener('click', onClick); element.parentNode.removeChild(element);例四:
var intervalId = null, params;function createChunks() {var div, foo, i, str;for (i = 0; i < 20; i++) {div = document.createElement("div");str = new Array(1000000).join('x');foo = {str: str,div: div};div.foo = foo;} }function start() {if (intervalId) {return;}intervalId = setInterval(createChunks, 1000); }function stop() {if (intervalId) {// 清除定時(shí)器clearInterval(intervalId);}// 清除變量intervalId = null; }鏈接觀察垃圾回收是怎么工作的—Google: Watching the GC work
在上面圖片中,可以觀察到,點(diǎn)擊 start 按鈕,內(nèi)存和節(jié)點(diǎn)數(shù)暴增,當(dāng)點(diǎn)擊stop 時(shí),垃圾收集器回收了這些定時(shí)器、變量等,從而釋放了內(nèi)存。
上期博客
- 重構(gòu)你的JS代碼
- 一些CSS3動(dòng)畫(huà)
我的github,歡迎star
超強(qiáng)干貨來(lái)襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的深入浅出 JavaScript 内存管理,垃圾回收的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 我的新宠Vue a系列 项目初构
- 下一篇: ExtJS + Gears