摘要:在中,由于垃圾回收是自動(dòng)進(jìn)行的,所以人們?cè)诰幋a時(shí)可能不太會(huì)注意這方面。時(shí),引擎統(tǒng)一對(duì)所有這些狀態(tài)的對(duì)象進(jìn)行回收。,表示釋放該對(duì)象后能得到的內(nèi)存大小。
在 JavaScript 中,由于垃圾回收是自動(dòng)進(jìn)行的,所以人們?cè)诰幋a時(shí)可能不太會(huì)注意這方面。但事實(shí)是,一些 webapp 在使用一段時(shí)間后,會(huì)出現(xiàn)卡頓的現(xiàn)象,特別是那些單頁應(yīng)用,包括 WebView 方式的手機(jī) app 。這個(gè)現(xiàn)象在傳統(tǒng)的“單擊 - 刷新”類型的頁面中并不明顯,因?yàn)轫撁嫠⑿轮?,所有沒有被回收的垃圾對(duì)象也會(huì)被清除,但是在單頁應(yīng)用中,如果沒有手動(dòng)去點(diǎn)瀏覽器的刷新按鈕,那么就算是很小的內(nèi)存泄露,隨著頁面停留時(shí)間的增長(zhǎng),累積的泄露會(huì)越來越多,在手機(jī)上的感覺就更明顯了。
所以這里想討論一下內(nèi)存泄露是如何發(fā)生的,以及如何去避免。
開門見山,一般有兩種方式的垃圾回收機(jī)制,一個(gè)是“引用計(jì)數(shù)”,當(dāng)一個(gè)對(duì)象被引用的次數(shù)為 0 時(shí),該對(duì)象就可以被回收;另一個(gè)是“標(biāo)記清除”,當(dāng)一個(gè)對(duì)象不能再被訪問到時(shí),對(duì)該對(duì)象進(jìn)行標(biāo)記,等下一輪 GC 事件發(fā)生時(shí),這些對(duì)象就會(huì)被清除。從 2012 年起,所有的現(xiàn)代瀏覽器都是基于“標(biāo)記清除”的回收算法,所以,如果需要兼容更早的瀏覽器,可能需要做更多的事。GC 的時(shí)機(jī)由 JS 引擎決定,需要知道的事,當(dāng) GC 進(jìn)行時(shí),主線程會(huì)被阻塞,這個(gè)時(shí)間可以通過 Chrome 的 Timeline 工具看到,最少也會(huì)超過 10 ms 吧。
Chrome DevTools - Timeline在 Chrome 中可以很直觀方便地看到垃圾回收事件的執(zhí)行。打開 Chrome 的 Timeline,只需要勾選“Memory”就可以了,并且在左邊的 View 中選中第二個(gè)。
然后單擊放大鏡下面的圓點(diǎn),這時(shí)候 Chrome 會(huì)開始記錄內(nèi)存分配、繪制等事件,等你打開一張頁面,比如百度吧,再單擊這個(gè)圓點(diǎn)(現(xiàn)在應(yīng)該是紅色的了),就會(huì)看到一條藍(lán)色的折線。不同頁面不一樣,但幾乎都會(huì)有一個(gè)突然下降的地方,比如下圖中 1200 ms 左邊的地方,單擊它,就能在下方顯示 GC 事件所用的時(shí)間,以及它回收了多少內(nèi)存。
如果你看到自己網(wǎng)站的這條藍(lán)色折線是呈上升趨勢(shì),在不斷的 GC 后,內(nèi)存還是在上升,就極有可能是發(fā)生了內(nèi)存泄露,需要排查一下代碼。
引用計(jì)數(shù)這里的問題在于“循環(huán)引用”,如果對(duì)象 a 的屬性引用了 b,而 b 的屬性引用了 a,由于引擎只有在變量的引用次數(shù)為 0 的情況下才會(huì)回收,這里 a 和 b 的引用次數(shù)至少有 1,所以就算它們所在的函數(shù)執(zhí)行完了,這兩個(gè)變量還是無法被回收掉。
function foo() { var a = {}, b = {}; a.attr = b; b.attr = a; } foo();
當(dāng) foo 函數(shù)執(zhí)行很多次之后,就會(huì)有很多個(gè)無法被回收的 a 和 b 存在。
實(shí)際情況可能是這樣的:
function foo() { var text = document.getElementById("input-text"); text.onfocus = function() { text.value = ""; } } foo();
意思是,當(dāng)光標(biāo)移到輸入框時(shí),清空原有的內(nèi)容??紤] text 變量和 foo 里面的匿名函數(shù),text 的 onfocus 屬性引用了匿名函數(shù),而該匿名函數(shù)引用了 text 變量(循環(huán)了),所以當(dāng) foo 執(zhí)行結(jié)束后,這兩個(gè)對(duì)象由于引用次數(shù)大于 0 而無法被回收。
對(duì)于這種情況,只需要在 foo 的末尾對(duì) text 變量置空就可以了。
text = null;
如果你用 Chrome 運(yùn)行這個(gè)例子的話,會(huì)看到藍(lán)線還是降到初始的高度了,因?yàn)?Chrome 是基于“標(biāo)記清除”的算法來回收內(nèi)存的,所以不會(huì)有“循環(huán)引用”的問題。
標(biāo)記清除對(duì)于標(biāo)記清除,心中要想象一個(gè)樹,每個(gè)頁面都存在一個(gè)根,每當(dāng)一個(gè)函數(shù)執(zhí)行,就會(huì)生成一個(gè)節(jié)點(diǎn)。自然,嵌套的函數(shù)調(diào)用就會(huì)有子節(jié)點(diǎn)。一般情況下(沒有閉包),當(dāng)函數(shù)執(zhí)行完時(shí),內(nèi)部的變量都是無法被其他代碼訪問的,所以它就被標(biāo)記為“無法被訪問”。GC 時(shí),JS 引擎統(tǒng)一對(duì)所有這些狀態(tài)的對(duì)象進(jìn)行回收。
介紹兩個(gè)概念。Shallow Size,表示該對(duì)象本身占用的內(nèi)存。Retained Size,表示釋放該對(duì)象后能得到的內(nèi)存大小。什么意思?比如上圖綠色的 #3,這個(gè)綠色的面積就是 Shallow Size。釋放 #3 后,#4 和 #5 也會(huì)被釋放,所以 Retained Size 就是 #3、#4、#5 的總大小。
在“標(biāo)記清除”算法中,難點(diǎn)是如何判斷一個(gè)對(duì)象已經(jīng)是“無法被訪問”了。
DOM 片段如果用樹去分析垃圾回收,會(huì)發(fā)現(xiàn)其實(shí)我們需要做的事情很少,因?yàn)楫?dāng)一個(gè)函數(shù)執(zhí)行完之后,它連帶的對(duì)象都會(huì)被清除。就算有閉包,當(dāng)引用該閉包的函數(shù)執(zhí)行完時(shí),這些閉包也同樣會(huì)被標(biāo)記。
那么在哪里會(huì)發(fā)生內(nèi)存泄露呢?看這個(gè)例子。
var btn = document.getElementById("btn"); btn.onclick = function() { var fragment = document.createElement("div"); }
它表示每單擊一次按鈕,就創(chuàng)建一個(gè) 這段代碼過后,雖然 從 DOM 中移除了,由于它的監(jiān)聽器還在,所以無法被 GC 回收。 要避免這種情況就是通過 removeEventListener 將回調(diào)函數(shù)去掉。 如果使用 setInterval,那么它引用到的變量的上下文會(huì)保留下來。 這里的情況時(shí),每隔 1 秒彈框一次。第一,雖然只用到了 name,但 name 所在的上下文都無法被釋放,包括 title 。第二,由于定時(shí)器一直在執(zhí)行,所以這個(gè)上下文是不會(huì)被釋放的。當(dāng)然,有時(shí)候這是業(yè)務(wù)要求,也談不上內(nèi)存泄露了,只不過要注意的是,如果真的有沒必要的定時(shí)器,請(qǐng)調(diào)用 clearInterval 把它去掉吧。 另一方面,你不用為了僅僅避免內(nèi)存泄露對(duì) setTimeout 調(diào)用 clearTimeout 。它是不會(huì)造成內(nèi)存泄露的,除非是別的什么原因,比如說,在 setTimeout 中遞歸調(diào)用了當(dāng)前定時(shí)器,這相當(dāng)于模擬 setInterval,可以與 setInterval 做類似處理。 在平時(shí)的一些開發(fā)過程中,我發(fā)現(xiàn)雖然在 Chrome 中發(fā)生了 GC 事件,并且內(nèi)存也降得很低,如果用 Profile 工具 Take Heap Snapshot 的話,也不會(huì)覺得有內(nèi)存泄露發(fā)生。但在手機(jī)上(WebView)的確會(huì)存在越用越卡的現(xiàn)象,這點(diǎn)可能要根據(jù)不同的環(huán)境來分析,但文中提到的關(guān)鍵兩個(gè)地方就是:解除引用,以及解除監(jiān)聽的事件。 如果自己的代碼中能做到這兩點(diǎn)的話,可能卡頓是由別的問題引起的,而不是內(nèi)存泄露。 文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。 轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/85932.html 摘要:每個(gè)構(gòu)造函數(shù)定義了一類對(duì)象,表示由構(gòu)造函數(shù)初始化對(duì)象的集合。嚴(yán)格模式下,明確禁止八進(jìn)制數(shù)。日期和時(shí)間構(gòu)造函數(shù)用來創(chuàng)建表示日期和時(shí)間的對(duì)象,包含方法。模式匹配函數(shù)是一個(gè)構(gòu)造函數(shù),創(chuàng)建正則表達(dá)式。布爾值表示兩種狀態(tài),使用保留字和。
《Javascript權(quán)威指南》就是前端工程師口中常說的犀牛書,得名是因?yàn)橹形姆g出版的書籍封面是一只犀牛,是學(xué)習(xí)JavaScript的必讀書籍。
JavaSc... 摘要:摘要是如何回收內(nèi)存的深入淺出系列深入淺出第課箭頭函數(shù)中的究竟是什么鬼深入淺出第課函數(shù)是一等公民是什么意思呢深入淺出第課什么是垃圾回收算法最近垃圾回收這個(gè)話題非常火,大家不能隨隨便便的扔垃圾了,還得先分類,這樣方便對(duì)垃圾進(jìn)行回收再利用。
摘要: JS是如何回收內(nèi)存的?
《JavaScript深入淺出》系列:
JavaScript深入淺出第1課:箭頭函數(shù)中的this究竟是什么鬼?
Jav... 摘要:它將堆內(nèi)存一分為二每一部分空間稱為。以的垃圾回收堆內(nèi)存為例做一次小的垃圾回收需要毫秒以上做一次非增量式的垃圾回收甚至要秒以上。這是垃圾回收中引起線程暫停執(zhí)行的時(shí)間在這樣的時(shí)間花銷下應(yīng)用的性能和響應(yīng)能力都會(huì)直線下降。
我們通常理解的 javascript 垃圾回收機(jī)制都停留在表面,會(huì)釋放不被引用變量?jī)?nèi)存,最近在讀《深入淺出node.js》的書,詳細(xì)了解了下 v8 垃圾回收的算法,記錄了一... 摘要:內(nèi)存回收此時(shí),局部變量就沒有存在的必要了,因此可以釋放它們的內(nèi)存以供將來使用。局部變量會(huì)在它們離開執(zhí)行環(huán)境時(shí)自動(dòng)被解除引用,如下面這個(gè)例子所示手工解除的引用由于局部變量在函數(shù)執(zhí)行完畢后就離開了其執(zhí)行環(huán)境,因此無需我們顯式地去為它解除引用。
JavaScript 具有自動(dòng)垃圾收集機(jī)制(GC:Garbage Collecation),也就是說,執(zhí)行環(huán)境會(huì)負(fù)責(zé)管理代碼執(zhí)行過程中使用的內(nèi)存。而... 摘要:所謂的內(nèi)存泄漏簡(jiǎn)單來說是不再用到的內(nèi)存,沒有及時(shí)釋放。如果一個(gè)值不再需要了,引用數(shù)卻不為,垃圾回收機(jī)制無法釋放這塊內(nèi)存,從而導(dǎo)致內(nèi)存泄漏。
前言
程序的運(yùn)行需要內(nèi)存。只要程序提出要求,操作系統(tǒng)或者運(yùn)行時(shí)就必須供給內(nèi)存。所謂的內(nèi)存泄漏簡(jiǎn)單來說是不再用到的內(nèi)存,沒有及時(shí)釋放。為了更好避免內(nèi)存泄漏,我們先介紹Javascript垃圾回收機(jī)制。
在C與C++等語言中,開發(fā)人員可以直接控制內(nèi)存的...var content = document.getElementById("content");
content.innerHTML = "";
var button = document.getElementById("button");
button.addEventListener("click", function() {});
content.innerHTML = "";
function foo() {
var name = "tom",
title = "Hero";
window.setInterval(function() {
alert(name);
}, 1000);
}
foo();
相關(guān)文章
《JavaScript權(quán)威指南》隨筆(一)
JavaScript深入淺出第3課:什么是垃圾回收算法?
javascript 垃圾回收算法
《JavaScript 闖關(guān)記》之垃圾回收和內(nèi)存管理
JavaScript中的垃圾回收和內(nèi)存泄漏
發(fā)表評(píng)論
0條評(píng)論
閱讀 3729·2023-04-25 17:45
閱讀 3438·2021-09-04 16:40
閱讀 1005·2019-08-30 13:54
閱讀 2137·2019-08-29 12:59
閱讀 1407·2019-08-26 12:11
閱讀 3284·2019-08-23 15:17
閱讀 1526·2019-08-23 12:07
閱讀 3888·2019-08-22 18:00