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