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