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