這篇文章主要講解了“怎么處理javascript內(nèi)存泄露”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么處理javascript內(nèi)存泄露”吧!
創(chuàng)新互聯(lián)建站于2013年創(chuàng)立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元陵城做網(wǎng)站,已為上家服務(wù),為陵城各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:028-86922220
處理方法:1、使用完成之后為其賦值null或者重新賦其他值;2、使用現(xiàn)代的垃圾回收算法;3、保存 DOM 元素引用的時(shí)候,要小心謹(jǐn)慎;4、通過SessionStack,回放應(yīng)用中的問題,以免造成內(nèi)存泄漏,也預(yù)防增加整個(gè)應(yīng)用的內(nèi)存占用。
本教程操作環(huán)境:windows7系統(tǒng)、javascript1.8.5版、Dell G3電腦。、
內(nèi)存泄露是每個(gè)開發(fā)者最終都要面對(duì)的問題,它是許多問題的根源:反應(yīng)遲緩,崩潰,高延遲,以及其他應(yīng)用問題。
本質(zhì)上,內(nèi)存泄露可以定義為:應(yīng)用程序不再需要占用內(nèi)存的時(shí)候,由于某些原因,內(nèi)存沒有被操作系統(tǒng)或可用內(nèi)存池回收。編程語言管理內(nèi)存的方式各不相同。只有開發(fā)者最清楚哪些內(nèi)存不需要了,操作系統(tǒng)可以回收。一些編程語言提供了語言特性,可以幫助開發(fā)者做此類事情。另一些則寄希望于開發(fā)者對(duì)內(nèi)存是否需要清晰明了。
JavaScript 是一種垃圾回收語言。垃圾回收語言通過周期性地檢查先前分配的內(nèi)存是否可達(dá),幫助開發(fā)者管理內(nèi)存。換言之,垃圾回收語言減輕了“內(nèi)存仍可用”及“內(nèi)存仍可達(dá)”的問題。兩者的區(qū)別是微妙而重要的:僅有開發(fā)者了解哪些內(nèi)存在將來仍會(huì)使用,而不可達(dá)內(nèi)存通過算法確定和標(biāo)記,適時(shí)被操作系統(tǒng)回收。
垃圾回收語言的內(nèi)存泄露主因是不需要的引用。理解它之前,還需了解垃圾回收語言如何辨別內(nèi)存的可達(dá)與不可達(dá)。
大部分垃圾回收語言用的算法稱之為 Mark-and-sweep 。算法由以下幾步組成:
垃圾回收器創(chuàng)建了一個(gè)“roots”列表。Roots 通常是代碼中全局變量的引用。JavaScript 中,“window” 對(duì)象是一個(gè)全局變量,被當(dāng)作 root 。window 對(duì)象總是存在,因此垃圾回收器可以檢查它和它的所有子對(duì)象是否存在(即不是垃圾);
所有的 roots 被檢查和標(biāo)記為激活(即不是垃圾)。所有的子對(duì)象也被遞歸地檢查。從 root 開始的所有對(duì)象如果是可達(dá)的,它就不被當(dāng)作垃圾。
所有未被標(biāo)記的內(nèi)存會(huì)被當(dāng)做垃圾,收集器現(xiàn)在可以釋放內(nèi)存,歸還給操作系統(tǒng)了。
現(xiàn)代的垃圾回收器改良了算法,但是本質(zhì)是相同的:可達(dá)內(nèi)存被標(biāo)記,其余的被當(dāng)作垃圾回收。
不需要的引用是指開發(fā)者明知內(nèi)存引用不再需要,卻由于某些原因,它仍被留在激活的 root 樹中。在 JavaScript 中,不需要的引用是保留在代碼中的變量,它不再需要,卻指向一塊本該被釋放的內(nèi)存。有些人認(rèn)為這是開發(fā)者的錯(cuò)誤。
為了理解 JavaScript 中最常見的內(nèi)存泄露,我們需要了解哪種方式的引用容易被遺忘。
JavaScript 處理未定義變量的方式比較寬松:未定義的變量會(huì)在全局對(duì)象創(chuàng)建一個(gè)新變量。在瀏覽器中,全局對(duì)象是 window
。
真相是:
函數(shù) foo
內(nèi)部忘記使用 var
,意外創(chuàng)建了一個(gè)全局變量。此例泄露了一個(gè)簡(jiǎn)單的字符串,無傷大雅,但是有更糟的情況。
另一種意外的全局變量可能由 this
創(chuàng)建:
在 JavaScript 文件頭部加上
'use strict'
,可以避免此類錯(cuò)誤發(fā)生。啟用嚴(yán)格模式解析 JavaScript ,避免意外的全局變量。
全局變量注意事項(xiàng)
盡管我們討論了一些意外的全局變量,但是仍有一些明確的全局變量產(chǎn)生的垃圾。它們被定義為不可回收(除非定義為空或重新分配)。尤其當(dāng)全局變量用于臨時(shí)存儲(chǔ)和處理大量信息時(shí),需要多加小心。如果必須使用全局變量存儲(chǔ)大量數(shù)據(jù)時(shí),確保用完以后把它設(shè)置為 null 或者重新定義。與全局變量相關(guān)的增加內(nèi)存消耗的一個(gè)主因是緩存。緩存數(shù)據(jù)是為了重用,緩存必須有一個(gè)大小上限才有用。高內(nèi)存消耗導(dǎo)致緩存突破上限,因?yàn)榫彺鎯?nèi)容無法被回收。
在 JavaScript 中使用 setInterval
非常平常。一段常見的代碼:
此例說明了什么:與節(jié)點(diǎn)或數(shù)據(jù)關(guān)聯(lián)的計(jì)時(shí)器不再需要,node
對(duì)象可以刪除,整個(gè)回調(diào)函數(shù)也不需要了??墒?,計(jì)時(shí)器回調(diào)函數(shù)仍然沒被回收(計(jì)時(shí)器停止才會(huì)被回收)。同時(shí),someResource
如果存儲(chǔ)了大量的數(shù)據(jù),也是無法被回收的。
對(duì)于觀察者的例子,一旦它們不再需要(或者關(guān)聯(lián)的對(duì)象變成不可達(dá)),明確地移除它們非常重要。老的 IE 6 是無法處理循環(huán)引用的。如今,即使沒有明確移除它們,一旦觀察者對(duì)象變成不可達(dá),大部分瀏覽器是可以回收觀察者處理函數(shù)的。
觀察者代碼示例:
對(duì)象觀察者和循環(huán)引用注意事項(xiàng)
老版本的 IE 是無法檢測(cè) DOM 節(jié)點(diǎn)與 JavaScript 代碼之間的循環(huán)引用,會(huì)導(dǎo)致內(nèi)存泄露。如今,現(xiàn)代的瀏覽器(包括 IE 和 Microsoft Edge)使用了更先進(jìn)的垃圾回收算法,已經(jīng)可以正確檢測(cè)和處理循環(huán)引用了。換言之,回收節(jié)點(diǎn)內(nèi)存時(shí),不必非要調(diào)用 removeEventListener
了。
有時(shí),保存 DOM 節(jié)點(diǎn)內(nèi)部數(shù)據(jù)結(jié)構(gòu)很有用。假如你想快速更新表格的幾行內(nèi)容,把每一行 DOM 存成字典(JSON 鍵值對(duì))或者數(shù)組很有意義。此時(shí),同樣的 DOM 元素存在兩個(gè)引用:一個(gè)在 DOM 樹中,另一個(gè)在字典中。將來你決定刪除這些行時(shí),需要把兩個(gè)引用都清除。
此外還要考慮 DOM 樹內(nèi)部或子節(jié)點(diǎn)的引用問題。假如你的 JavaScript 代碼中保存了表格某一個(gè) <td>
的引用。將來決定刪除整個(gè)表格的時(shí)候,直覺認(rèn)為 GC 會(huì)回收除了已保存的 <td>
以外的其它節(jié)點(diǎn)。實(shí)際情況并非如此:此<td>
是表格的子節(jié)點(diǎn),子元素與父元素是引用關(guān)系。由于代碼保留了 <td>
的引用,導(dǎo)致整個(gè)表格仍待在內(nèi)存中。保存 DOM 元素引用的時(shí)候,要小心謹(jǐn)慎。
閉包是 JavaScript 開發(fā)的一個(gè)關(guān)鍵方面:匿名函數(shù)可以訪問父級(jí)作用域的變量。
代碼示例:
代碼片段做了一件事情:每次調(diào)用 replaceThing
,theThing
得到一個(gè)包含一個(gè)大數(shù)組和一個(gè)新閉包(someMethod
)的新對(duì)象。同時(shí),變量 unused
是一個(gè)引用 originalThing
的閉包(先前的 replaceThing
又調(diào)用了 theThing
)。思緒混亂了嗎?最重要的事情是,閉包的作用域一旦創(chuàng)建,它們有同樣的父級(jí)作用域,作用域是共享的。someMethod
可以通過 theThing
使用,someMethod
與 unused
分享閉包作用域,盡管 unused
從未使用,它引用的 originalThing
迫使它保留在內(nèi)存中(防止被回收)。當(dāng)這段代碼反復(fù)運(yùn)行,就會(huì)看到內(nèi)存占用不斷上升,垃圾回收器(GC)并無法降低內(nèi)存占用。本質(zhì)上,閉包的鏈表已經(jīng)創(chuàng)建,每一個(gè)閉包作用域攜帶一個(gè)指向大數(shù)組的間接的引用,造成嚴(yán)重的內(nèi)存泄露。
Meteor 的博文 解釋了如何修復(fù)此種問題。在
replaceThing
的最后添加originalThing = null
。
Chrome 提供了一套很棒的檢測(cè) JavaScript 內(nèi)存占用的工具。與內(nèi)存相關(guān)的兩個(gè)重要的工具:timeline
和 profiles
。
timeline 可以檢測(cè)代碼中不需要的內(nèi)存。在此截圖中,我們可以看到潛在的泄露對(duì)象穩(wěn)定的增長(zhǎng),數(shù)據(jù)采集快結(jié)束時(shí),內(nèi)存占用明顯高于采集初期,Node(節(jié)點(diǎn))的總量也很高。種種跡象表明,代碼中存在 DOM 節(jié)點(diǎn)泄露的情況。
Profiles 是你可以花費(fèi)大量時(shí)間關(guān)注的工具,它可以保存快照,對(duì)比 JavaScript 代碼內(nèi)存使用的不同快照,也可以記錄時(shí)間分配。每一次結(jié)果包含不同類型的列表,與內(nèi)存泄露相關(guān)的有 summary(概要) 列表和 comparison(對(duì)照) 列表。
summary(概要) 列表展示了不同類型對(duì)象的分配及合計(jì)大?。簊hallow size(特定類型的所有對(duì)象的總大小),retained size(shallow size 加上其它與此關(guān)聯(lián)的對(duì)象大小)。它還提供了一個(gè)概念,一個(gè)對(duì)象與關(guān)聯(lián)的 GC root 的距離。
對(duì)比不同的快照的 comparison list 可以發(fā)現(xiàn)內(nèi)存泄露。
實(shí)質(zhì)上有兩種類型的泄露:周期性的內(nèi)存增長(zhǎng)導(dǎo)致的泄露,以及偶現(xiàn)的內(nèi)存泄露。顯而易見,周期性的內(nèi)存泄露很容易發(fā)現(xiàn);偶現(xiàn)的泄露比較棘手,一般容易被忽視,偶爾發(fā)生一次可能被認(rèn)為是優(yōu)化問題,周期性發(fā)生的則被認(rèn)為是必須解決的 bug。
以 Chrome 文檔中的代碼為例:
當(dāng) grow
執(zhí)行的時(shí)候,開始創(chuàng)建 p 節(jié)點(diǎn)并插入到 DOM 中,并且給全局變量分配一個(gè)巨大的數(shù)組。通過以上提到的工具可以檢測(cè)到內(nèi)存穩(wěn)定上升。
timeline 標(biāo)簽擅長(zhǎng)做這些。在 Chrome 中打開例子,打開 Dev Tools ,切換到 timeline,勾選 memory 并點(diǎn)擊記錄按鈕,然后點(diǎn)擊頁(yè)面上的 The Button
按鈕。過一陣停止記錄看結(jié)果:
兩種跡象顯示出現(xiàn)了內(nèi)存泄露,圖中的 Nodes(綠線)和 JS heap(藍(lán)線)。Nodes 穩(wěn)定增長(zhǎng),并未下降,這是個(gè)顯著的信號(hào)。
JS heap 的內(nèi)存占用也是穩(wěn)定增長(zhǎng)。由于垃圾收集器的影響,并不那么容易發(fā)現(xiàn)。圖中顯示內(nèi)存占用忽漲忽跌,實(shí)際上每一次下跌之后,JS heap 的大小都比原先大了。換言之,盡管垃圾收集器不斷的收集內(nèi)存,內(nèi)存還是周期性的泄露了。
確定存在內(nèi)存泄露之后,我們找找根源所在。
切換到 Chrome Dev Tools 的 profiles 標(biāo)簽,刷新頁(yè)面,等頁(yè)面刷新完成之后,點(diǎn)擊 Take Heap Snapshot 保存快照作為基準(zhǔn)。而后再次點(diǎn)擊 The Button
按鈕,等數(shù)秒以后,保存第二個(gè)快照。
篩選菜單選擇 Summary,右側(cè)選擇 Objects allocated between Snapshot 1 and Snapshot 2,或者篩選菜單選擇 Comparison ,然后可以看到一個(gè)對(duì)比列表。
此例很容易找到內(nèi)存泄露,看下 (string)
的 Size Delta
Constructor,8MB,58個(gè)新對(duì)象。新對(duì)象被分配,但是沒有釋放,占用了8MB。
如果展開 (string)
Constructor,會(huì)看到許多單獨(dú)的內(nèi)存分配。選擇某一個(gè)單獨(dú)的分配,下面的 retainers 會(huì)吸引我們的注意。
我們已選擇的分配是數(shù)組的一部分,數(shù)組關(guān)聯(lián)到 window
對(duì)象的 x
變量。這里展示了從巨大對(duì)象到無法回收的 root(window
)的完整路徑。我們已經(jīng)找到了潛在的泄露以及它的出處。
我們的例子還算簡(jiǎn)單,只泄露了少量的 DOM 節(jié)點(diǎn),利用以上提到的快照很容易發(fā)現(xiàn)。對(duì)于更大型的網(wǎng)站,Chrome 還提供了 Record Heap Allocations 功能。
回到 Chrome Dev Tools 的 profiles 標(biāo)簽,點(diǎn)擊 Record Heap Allocations。工具運(yùn)行的時(shí)候,注意頂部的藍(lán)條,代表了內(nèi)存分配,每一秒有大量的內(nèi)存分配。運(yùn)行幾秒以后停止。
上圖中可以看到工具的殺手锏:選擇某一條時(shí)間線,可以看到這個(gè)時(shí)間段的內(nèi)存分配情況。盡可能選擇接近峰值的時(shí)間線,下面的列表僅顯示了三種 constructor:其一是泄露最嚴(yán)重的(string)
,下一個(gè)是關(guān)聯(lián)的 DOM 分配,最后一個(gè)是 Text
constructor(DOM 葉子節(jié)點(diǎn)包含的文本)。
從列表中選擇一個(gè) HTMLpElement
constructor,然后選擇 Allocation stack
。
現(xiàn)在知道元素被分配到哪里了吧(grow
-> createSomeNodes
),仔細(xì)觀察一下圖中的時(shí)間線,發(fā)現(xiàn) HTMLpElement
constructor 調(diào)用了許多次,意味著內(nèi)存一直被占用,無法被 GC 回收,我們知道了這些對(duì)象被分配的確切位置(createSomeNodes
)?;氐酱a本身,探討下如何修復(fù)內(nèi)存泄露吧。
在 heap allocations 的結(jié)果區(qū)域,選擇 Allocation。
這個(gè)視圖呈現(xiàn)了內(nèi)存分配相關(guān)的功能列表,我們立刻看到了 grow
和 createSomeNodes
。當(dāng)選擇 grow
時(shí),看看相關(guān)的 object constructor,清楚地看到 (string)
, HTMLpElement
和 Text
泄露了。
結(jié)合以上提到的工具,可以輕松找到內(nèi)存泄露。
感謝各位的閱讀,以上就是“怎么處理javascript內(nèi)存泄露”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)怎么處理javascript內(nèi)存泄露這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
名稱欄目:怎么處理javascript內(nèi)存泄露
URL網(wǎng)址:http://jinyejixie.com/article46/ghddhg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、品牌網(wǎng)站建設(shè)、網(wǎng)站維護(hù)、營(yíng)銷型網(wǎng)站建設(shè)、網(wǎng)站收錄、靜態(tài)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)