摘要:摘要是如何回收內存的深入淺出系列深入淺出第課箭頭函數中的究竟是什么鬼深入淺出第課函數是一等公民是什么意思呢深入淺出第課什么是垃圾回收算法最近垃圾回收這個話題非常火,大家不能隨隨便便的扔垃圾了,還得先分類,這樣方便對垃圾進行回收再利用。
摘要: JS是如何回收內存的?
《JavaScript深入淺出》系列:
JavaScript深入淺出第1課:箭頭函數中的this究竟是什么鬼?
JavaScript深入淺出第2課:函數是一等公民是什么意思呢?
JavaScript深入淺出第3課:什么是垃圾回收算法?
最近垃圾回收這個話題非常火,大家不能隨隨便便的扔垃圾了,還得先分類,這樣方便對垃圾進行回收再利用。
其實,對于寫代碼來說,也有垃圾回收(garbage collection)這個問題,這里所說的垃圾,指的是程序中不再需要的內存空間,垃圾回收指的是回收這些不再需要的內存空間,讓程序可以重新利用這些釋放的內存空間。
手動管理內存對于C這種底層語言來說,我們可以使用malloc()函數分配內存空間,當所分配的內存不再需要的時候,可以使用free()函數來釋放內存空間。
#include#include #define TRUE 1 int main () { int *p, i, n, sum; while (TRUE) { printf ("請輸入數組長度: "); scanf ("%d", &n); p = (int *) malloc (n * sizeof (int)); // 分配內存空間 sum = 0; for (i = 0; i < n; ++i) { *(p + i) = i + 1; sum += *(p + i); } printf ("sum = %d ", sum); free (p); // 釋放內存空間 } return 0; }
示例代碼很簡單,輸入一個整數n,程序計算1、2、3...n的和。大家可以在Online C Compiler上運行這段代碼。
請輸入數組長度: 36 sum = 666 請輸入數組長度: 100 sum = 5050
如果我們不去調用free()函數釋放內存的話,就會導致內存泄漏(memory leak)。每個while循環中,指針p都會指向新分配的內存空間。而p之前指向的內存空間雖然沒用了,但是并不會被釋放,除非程序退出。如果while循環一直執行下去的話,內存早晚不夠用。
垃圾回收算法如果讓我們去手動管理內存,那不知道要寫出多少BUG,內存分分鐘用完。還好現代編程語言,比如Java, Python, Go以及JavaScript,都是支持自動垃圾回收的。也就是說,這些語言可以自動回收程序不再需要的內存空間,這樣既減輕了開發者的負擔,也有效避免了內存泄漏。
其實,早在C語言誕生之前的1960年,圖靈獎得主John McCarthy就在Lisp語言中實現了自動垃圾回收算法。算法本身其實非常簡單,標記那些程序訪問不到的數據,回收它們的內存空間。但是,垃圾回收算法把程序員從硬件層(內存管理)解放出來了,這種理念還是很先進的。
對于垃圾回收算法來說,最困難的問題是如何確定哪些內存空間是可以回收的,即哪些內存空間是程序不再需要的,這是一個不可判定問題(undecidable problem)。所謂不可判定,就是沒有哪個垃圾回收算法可以確定程序中所有可以回收的內存空間。
McCarthy簡化了判定數據是否需要的問題,將其簡化為判斷數據是否能夠訪問。如果程序已經不能訪問某個數據了,那這個數據自然是不再需要了。但是,這個邏輯反過來是不成立的,一些可以訪問的數據也有可能其實程序已經不再需要了。
McCarthy的垃圾回收算法現在通常被稱作Mark-and-Sweep,它是現在很多語言(Java, JavaScript, Go)的垃圾回收算法的原型。
JavaScript的垃圾回收算法對于JavaScript來說,我們是不需要手動管理內存的,因為JavaScript引擎例如V8與SpiderMonkey都會自動分配并回收內存。
比較古老的瀏覽器,比如IE6和IE7使用的垃圾回收算法是reference-counting:確定對象是否被引用,沒有被引用的對象則可以回收。這個算法無法回收Circular Object,有可能會因此造成內存泄漏:
var div; window.onload = function() { div = document.getElementById("myDivElement"); div.circularReference = div; div.lotsOfData = new Array(10000).join("*"); };
div對象的circularReference屬性指向div本身,因此div對象始終“被引用”。如果使用reference-counting垃圾回收算法的話,則div對象永遠不會被回收。最新的瀏覽器很早就不再使用reference-counting,因此Circular Object無法回收的問題也就不存在了。
目前,主流的瀏覽器使用的垃圾回收算法都是基于mark-and-sweep:
root對象包括全局對象以及程序當前的執行堆棧;
從root對象出發,遍歷其所有子對象,能夠通過遍歷訪問到的對象是可以訪問的;
其他不能遍歷對象是不可訪問的,其內存空間可以回收;
算法思想并沒有超越McCarthy半個世紀之前的設計,只是在實現細節上做了大量的優化,V8的垃圾回收模塊Orinoco大致是這樣做的:
采用多線程的方式進行垃圾回收,盡量避免對JavaScript本身的代碼執行造成暫停;
利用瀏覽器渲染頁面的空閑時間進行垃圾回收;
根據The Generational Hypothesis,大多數對象的生命周期非常短暫,因此可以將對象根據生命周期進行區分,生命周期短的對象與生命周期長的對象采用不同的方式進行垃圾回收;
對內存空間進行整理,消除內存碎片化,最大化利用釋放的內存空間;
JS引擎的垃圾回收算法已經非常強大了,所以我們作為JavaScript開發者基本上感受不到它的存在。
觀察JavaScript垃圾回收算法我們通過Chrome開發者工具實際感受一下垃圾回收算法的效果。
測試1:
var str = new Array(100000000).join("*"); setInterval(() => { console.log(str[0]); }, 1000);
str是一個超長字符串,因此會占有不少的內存空間。代碼里面寫了一個setInterval,是為了讓這段代碼永遠執行下去,程序不退出。這樣的話,字符串str永遠在使用中,永遠是可以訪問的,那它的內存空間就不會被回收。
我使用的是Chrome 75,在其開發者工具的Memory的Tab下,使用Take heap snapshot可以獲取內存快照:
可知,內存占用了97MB,且我們可以在其中找到str這個超長字符串。
測試2
var str = new Array(100000000).join("*"); setInterval(() => { console.log(str[0]); }, 1000); setTimeout(() => { str = "******"; }, 10000);
在setTimeout的回調函數中,我們對str進行了重新賦值,這就意味著之前的超長字符串就不可訪問了,那它的內存空間就會被回收。
在代碼運行10s之后,即str重新賦值之后進行快照:
可知,內存只占用了1.6MB,且我們可以在其中找到str字符串,它的長度只有6,因此占用的內存空間非常小。
想象一下,如果不再需要的內存空間不會被回收的話,1T的內存都不夠用。
關于JS,我打算花1年時間寫一個系列的博客JavaScript深入淺出,大家還有啥不太清楚的地方?不妨留言一下,我可以研究一下,然后再與大家分享一下。歡迎添加我的個人微信(KiwenLau),我是Fundebug的技術負責人,一個對JS又愛又恨的程序員。
參考MDN:Memory Management
為什么Lisp語言如此先進?
Recursive Functions of Symbolic Expressions Their Computation by Machine(Part I)
Trash talk: the Orinoco garbage collector
Idle-Time Garbage-Collection Scheduling
關于FundebugFundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有陽光保險、核桃編程、荔枝FM、掌門1對1、微脈、青團社等眾多品牌企業。歡迎大家免費試用!
版權聲明轉載時請注明作者 Fundebug以及本文地址:
https://blog.fundebug.com/2019/07/03/javascript-garbage-collection/
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105275.html
摘要:摘要性能彪悍的引擎。深入淺出系列深入淺出第課箭頭函數中的究竟是什么鬼深入淺出第課函數是一等公民是什么意思呢深入淺出第課什么是垃圾回收算法深入淺出第課是如何工作的最近,生態系統又多了個非常硬核的項目。 摘要: 性能彪悍的V8引擎。 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數中的this究竟是什么鬼? JavaScript深入淺出第2課:函數是一...
摘要:所做的最重要的事情,就是對成千上萬的網頁進行排序,所以它存在的意義是基于網頁的。確實有很多非常成功的產品,比如,,,但是它們其實都是收購來的。為什么呢因為要做到極簡主義,需要深刻思考用戶需求以及產品價值。 摘要: Chrome改變世界。 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數中的this究竟是什么鬼? JavaScript深入淺出第2課:...
摘要:垃圾回收內存管理實踐先通過一個來看看在中進行垃圾回收的過程是怎樣的內存泄漏識別在環境里提供了方法用來查看當前進程內存使用情況,單位為字節中保存的進程占用的內存部分,包括代碼本身棧堆。 showImg(https://segmentfault.com/img/remote/1460000019894672?w=640&h=426);作者 | 五月君Node.js 技術棧 | https:...
摘要:引擎對堆內存中的對象進行分代管理新生代存活周期較短的對象,如臨時變量字符串等。內存泄漏對于持續運行的服務進程,必須及時釋放不再用到的內存。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第一期,本周的主題是調用堆棧,今天是第4天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計劃...
摘要:本期推薦文章類內存泄漏及如何避免,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。四種常見的內存泄漏劃重點這是個考點意外的全局變量未定義的變量會在全局對象創建一個新變量,如下。因為老版本的是無法檢測節點與代碼之間的循環引用,會導致內存泄漏。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第一期,本周的主題...
閱讀 1355·2021-11-15 11:45
閱讀 3122·2021-09-27 13:36
閱讀 2866·2019-08-30 15:54
閱讀 984·2019-08-29 12:38
閱讀 2905·2019-08-29 11:22
閱讀 2983·2019-08-26 13:52
閱讀 2024·2019-08-26 13:30
閱讀 583·2019-08-26 10:37