摘要:解決方式是,當我們不使用它們的時候,手動切斷鏈接淘汰把和對象轉為了真正的對象,避免了使用這種垃圾收集策略,消除了以下常見的內(nèi)存泄漏的主要原因。以上參考資料高程垃圾收集類內(nèi)存泄漏及如何避免內(nèi)存泄露及解決方案詳解類內(nèi)存泄漏及如何避免
前言
起因是因為想了解閉包的內(nèi)存泄露機制,然后想起《js高級程序設計》中有關于垃圾回收機制的解析,之前沒有很懂,過一年回頭再看就懂了,寫篇博客與大家分享一下。如果喜歡的話可以點波贊/關注,支持一下。
個人博客了解一下:obkoro1.com內(nèi)存的生命周期:
分配你所需要的內(nèi)存:
由于字符串、對象等沒有固定的大小,js程序在每次創(chuàng)建字符串、對象的時候,程序都會分配內(nèi)存來存儲那個實體。
使用分配到的內(nèi)存做點什么。
不需要時將其釋放回歸:
在不需要字符串、對象的時候,需要釋放其所占用的內(nèi)存,否則將會消耗完系統(tǒng)中所有可用的內(nèi)存,造成系統(tǒng)崩潰,這就是垃圾回收機制所存在的意義。
所謂的內(nèi)存泄漏指的是:由于疏忽或錯誤造成程序未能釋放那些已經(jīng)不再使用的內(nèi)存,造成內(nèi)存的浪費。
垃圾回收機制:在C和C++之類的語言中,需要手動來管理內(nèi)存的,這也是造成許多不必要問題的根源。幸運的是,在編寫js的過程中,內(nèi)存的分配以及內(nèi)存的回收完全實現(xiàn)了自動管理,我們不用操心這種事情。
垃圾收集機制的原理:垃圾收集器會按照固定的時間間隔,周期性的找出不再繼續(xù)使用的變量,然后釋放其占用的內(nèi)存。
什么叫不再繼續(xù)使用的變量?
不再使用的變量也就是生命周期結束的變量,是局部變量,局部變量只在函數(shù)的執(zhí)行過程中存在,當函數(shù)運行結束,沒有其他引用(閉包),那么該變量會被標記回收。
全局變量的生命周期直至瀏覽器卸載頁面才會結束,也就是說全局變量不會被當成垃圾回收。
標記清除:當前采用的垃圾收集策略工作原理:
當變量進入環(huán)境時(例如在函數(shù)中聲明一個變量),將這個變量標記為“進入環(huán)境”,當變量離開環(huán)境時,則將其標記為“離開環(huán)境”。標記“離開環(huán)境”的就回收內(nèi)存。
工作流程:
垃圾收集器會在運行的時候會給存儲在內(nèi)存中的所有變量都加上標記。
去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標記。
那些還存在標記的變量被視為準備刪除的變量。
最后垃圾收集器會執(zhí)行最后一步內(nèi)存清除的工作,銷毀那些帶標記的值并回收它們所占用的內(nèi)存空間。
到2008年為止,IE、Chorme、Fireofx、Safari、Opera 都使用標記清除式的垃圾收集策略,只不過垃圾收集的時間間隔互有不同。
引用計數(shù)略:被廢棄的垃圾收集策略循環(huán)引用:跟蹤記錄每個值被引用的技術
在老版本的瀏覽器中(對,又是IE),IE9以下BOM和DOM對象就是使用C++以COM對象的形式實現(xiàn)的。
COM的垃圾收集機制采用的就是引用計數(shù)策略,這種機制在出現(xiàn)循環(huán)引用的時候永遠都釋放不掉內(nèi)存。
var element = document.getElementById("something"); var myObject = new Object(); myObject.element = element; // element屬性指向dom element.someThing = myObject; // someThing回指myObject 出現(xiàn)循環(huán)引用(兩個對象一直互相包含 一直存在計數(shù))。
解決方式是,當我們不使用它們的時候,手動切斷鏈接:
myObject.element = null; element.someThing = null;
淘汰:
IE9把BOM和DOM對象轉為了真正的js對象,避免了使用這種垃圾收集策略,消除了IE9以下常見的內(nèi)存泄漏的主要原因。
IE7以下有一個聲明狼藉的性能問題,大家了解一下:
256個變量,4096個對象(或數(shù)組)字面或者64KB的字符串,達到任何一個臨界值會觸發(fā)垃圾收集器運行。
如果一個js腳本的生命周期一直保有那么多變量,垃圾收集器會一直頻繁的運行,引發(fā)嚴重的性能問題。
IE7已修復這個問題。
哪些情況會引起內(nèi)存泄漏?雖然有垃圾回收機制,但我們在編寫代碼的時候,有些情況還是會造成內(nèi)存泄漏,了解這些情況,并在編寫程序的時候,注意避免,我們的程序會更具健壯性。
意外的全局變量:上文我們提到了全局變量不會被當成垃圾回收,我們在編碼中有時會出現(xiàn)下面這種情況:
function foo() { this.bar2 = "默認綁定this指向全局" // 全局變量=> window.bar2 bar = "全局變量"; // 沒有聲明變量 實際上是全局變量=>window.bar } foo();
當我們使用默認綁定,this會指向全局,this.something也會創(chuàng)建一個全局變量,這一點可能很多人沒有注意到。
解決方法:在函數(shù)內(nèi)使用嚴格模式or細心一點
function foo() { "use strict"; this.bar2 = "嚴格模式下this指向undefined"; bar = "報錯"; } foo();
當然我們也可以手動釋放全局變量的內(nèi)存:
window.bar = undefined delete window.bar2被遺忘的定時器和回調(diào)函數(shù)
當不需要setInterval或者setTimeout時,定時器沒有被clear,定時器的回調(diào)函數(shù)以及內(nèi)部依賴的變量都不能被回收,造成內(nèi)存泄漏。
var someResource = getData(); setInterval(function() { var node = document.getElementById("Node"); if(node) { node.innerHTML = JSON.stringify(someResource)); // 定時器也沒有清除 } // node、someResource 存儲了大量數(shù)據(jù) 無法回收 }, 1000);
解決方法: 在定時器完成工作的時候,手動清除定時器。
閉包:閉包可以維持函數(shù)內(nèi)局部變量,使其得不到釋放,造成內(nèi)存泄漏。
function bindEvent() { var obj = document.createElement("XXX"); var unused = function () { console.log(obj,"閉包內(nèi)引用obj obj不會被釋放"); }; // obj = null; }
解決方法:手動解除引用,obj = null。
循環(huán)引用問題就是IE9以下的循環(huán)引用問題,上文講過了。
沒有清理DOM元素引用:var refA = document.getElementById("refA"); document.body.removeChild(refA); // dom刪除了 console.log(refA, "refA"); // 但是還存在引用 能console出整個div 沒有被回收
不信的話,可以看下這個dom。
解決辦法:refA = null;
console保存大量數(shù)據(jù)在內(nèi)存中。過多的console,比如定時器的console會導致瀏覽器卡死。
解決:合理利用console,線上項目盡量少的使用console,當然如果你要發(fā)招聘除外。
如何避免內(nèi)存泄漏:記住一個原則:不用的東西,及時歸還,畢竟你是"借的"嘛。
減少不必要的全局變量,使用嚴格模式避免意外創(chuàng)建全局變量。
在你使用完數(shù)據(jù)后,及時解除引用(閉包中的變量,dom引用,定時器清除)。
組織好你的邏輯,避免死循環(huán)等造成瀏覽器卡頓,崩潰的問題。
關于內(nèi)存泄漏:即使是1byte的內(nèi)存,也叫內(nèi)存泄漏,并不一定是導致瀏覽器崩潰、卡頓才能叫做內(nèi)存泄漏。
一般是堆區(qū)內(nèi)存泄漏,棧區(qū)不會泄漏。
基本類型的值存在內(nèi)存中,被保存在棧內(nèi)存中,引用類型的值是對象,保存在堆內(nèi)存中。所以對象、數(shù)組之類的,才會發(fā)生內(nèi)存泄漏。
使用chrome監(jiān)控內(nèi)存泄漏,可以看一下這篇文章
結語了解了內(nèi)存泄漏的原因以及出現(xiàn)的情況,那么我們在編碼過程中只要多加注意,就不會發(fā)生非常嚴重的內(nèi)存泄漏問題。
PS:目前離職中,有坑位可以介紹一下,base:上海2號線淞虹路。
希望看完的朋友可以點個喜歡/關注,您的支持是對我最大的鼓勵。個人blog and 掘金個人主頁,如需轉載,請放上原文鏈接并署名。碼字不易,感謝支持!
如果喜歡本文的話,歡迎關注我的訂閱號,漫漫技術路,期待未來共同學習成長。
以上2018.7.7
參考資料:
JS高程4.3垃圾收集
4類 JavaScript 內(nèi)存泄漏及如何避免
JavaScript內(nèi)存泄露及解決方案詳解
4類 JavaScript 內(nèi)存泄漏及如何避免
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/96020.html
摘要:但是如果一個值不再用到了,引用次數(shù)卻不為,垃圾回收機制卻無法釋放這塊內(nèi)存,從而導致內(nèi)存泄漏。內(nèi)存泄漏垃圾回收語言的內(nèi)存泄漏主因是不需要的引用。常見內(nèi)存泄漏意外的全局變量處理未定義變量的方式比較寬松未定義的變量會在全局對象創(chuàng)建一個新變量。 簡答題: settimeout 與 setInterval的區(qū)別, 及對他們的內(nèi)存的分析 區(qū)別 setTimeout是在一段時間后調(diào)用指定函數(shù)(僅一...
摘要:但是如果一個值不再用到了,引用次數(shù)卻不為,垃圾回收機制卻無法釋放這塊內(nèi)存,從而導致內(nèi)存泄漏。內(nèi)存泄漏垃圾回收語言的內(nèi)存泄漏主因是不需要的引用。常見內(nèi)存泄漏意外的全局變量處理未定義變量的方式比較寬松未定義的變量會在全局對象創(chuàng)建一個新變量。 簡答題: settimeout 與 setInterval的區(qū)別, 及對他們的內(nèi)存的分析 區(qū)別 setTimeout是在一段時間后調(diào)用指定函數(shù)(僅一...
摘要:但是如果一個值不再用到了,引用次數(shù)卻不為,垃圾回收機制卻無法釋放這塊內(nèi)存,從而導致內(nèi)存泄漏。內(nèi)存泄漏垃圾回收語言的內(nèi)存泄漏主因是不需要的引用。常見內(nèi)存泄漏意外的全局變量處理未定義變量的方式比較寬松未定義的變量會在全局對象創(chuàng)建一個新變量。 簡答題: settimeout 與 setInterval的區(qū)別, 及對他們的內(nèi)存的分析 區(qū)別 setTimeout是在一段時間后調(diào)用指定函數(shù)(僅一...
摘要:一前言的垃圾回收機制使用垃圾回收機制來自動管理內(nèi)存。垃圾回收器只會針對新生代內(nèi)存區(qū)老生代指針區(qū)以及老生代數(shù)據(jù)區(qū)進行垃圾回收。分別對新生代和老生代使用不同的垃圾回收算法來提升垃圾回收的效率。 V8 實現(xiàn)了準確式 GC,GC 算法采用了分代式垃圾回收機制。因此,V8 將內(nèi)存(堆)分為新生代和老生代兩部分。 一、前言 V8的垃圾回收機制:JavaScript使用垃圾回收機制來自動管理內(nèi)存。垃...
摘要:局部變量只在函數(shù)執(zhí)行過程中存在。此時,局部變量就沒有存在的必要了,因此可以釋放它們的內(nèi)存以供將來使用??偨Y一般情況下,局部變量的生命周期為函數(shù)對象執(zhí)行到執(zhí)行結束,全局變量的生命周期為瀏覽器打開和關閉。 垃圾收集 JavaScript具有自動垃圾收集機制,也就是說,執(zhí)行環(huán)境會負責管理代碼執(zhí)行中使用的內(nèi)存。在C和C++語言中,開發(fā)人員一項基本任務就是手工跟蹤內(nèi)存的使用情況,這是造成許多問題...
閱讀 1328·2021-09-22 15:00
閱讀 3320·2019-08-30 14:00
閱讀 1235·2019-08-29 17:27
閱讀 1231·2019-08-29 16:35
閱讀 705·2019-08-29 16:14
閱讀 2051·2019-08-26 13:43
閱讀 2133·2019-08-26 11:35
閱讀 2317·2019-08-23 15:34