內存基本概念javascript
內存的生命週期:html
一、分配所需的內存java
二、內存的讀與寫c++
三、不須要時將其釋放chrome
全部語言的內存生命週期都基本一致,不一樣的是最後一步在低級語言中很清晰,可是在像JavaScript 等高級語言中,這一步是隱藏的、透明的。數組
js的內存生命週期:瀏覽器
一、定義變量時就完成了內存分配網絡
二、使用值的過程其實是對分配內存進行讀取與寫入的操做。讀取與寫入多是寫入一個變量或者一個對象的屬性值,甚至傳遞函數的參數。異步
三、而內存的釋放而依賴GC機制(高級語言解釋器嵌入的「垃圾回收器」)。async
程序運行的時候,須要內存空間存放數據。通常來講,系統會劃分出兩種不一樣的內存空間:一種叫作棧(stack),另外一種叫作堆(heap)。
堆(heap)與棧(stack)
heap是沒有結構的,數據能夠任意存放。heap用於複雜數據類型(引用類型)分配空間,例如數組對象、object對象。
stack是有結構的,每一個區塊按照必定次序存放(後進先出),stack中主要存放一些基本類型的變量和對象的引用,存在棧中的數據大小與生存期必須是肯定的。能夠明確知道每一個區塊的大小,所以,stack的尋址速度要快於heap。
函數調用造成了一個棧幀。
function foo(b) { var a = 10; return a + b + 11; } function bar(x) { var y = 3; return foo(x * y); } console.log(bar(7));
當調用bar
時,建立了第一個幀 ,幀中包含了bar
的參數和局部變量。
當bar
調用foo
時,第二個幀就被建立,並被壓到第一個幀之上,幀中包含了foo
的參數和局部變量。當foo
返回時,最上層的幀就被彈出棧(剩下bar
函數的調用幀 )。
當bar
返回的時候,棧就空了。
堆與棧的大小
程序運行時,每一個線程分配一個stack,每一個進程分配一個heap,也就是說,stack是線程獨佔的,heap是線程共用的。此外,stack建立的時候,大小是肯定的,數據超過這個大小,就發生stack overflow錯誤,而heap的大小是不肯定的,須要的話能夠不斷增長。因此這裏只看stack的大小限制。下面是一個簡單的測試:
var i=0; function inc() { i++; if(i>41909){return;} inc(); } inc();
測試環境是16G內存的電腦,須要注意的是:根據棧的定義能夠知道若是 inc 函數裏有變量申明的話也是會有內存佔用的。
一、谷歌瀏覽器chrome 55.0版本下限制是41909條。
二、IE8瀏覽器下限制是3062條。
stack overflow(棧溢出)
由於stack是有限制的,並且stack超出瀏覽器的規定的棧限制時就會報stack overflow。通常狀況下不會出現這種狀況,由於js語言有他本身的GC機制,而出現這種狀況通常是js的死循環或者沒有正確的中止遞歸形成的,能夠經過調試去追蹤stack。我還碰到過c++編繹的activx控件,使用事件函數作實時推送時stack overflow。緣由是控件的事件函數並不會等showMsg函數執行完再進行推送,解決方法是推送每次只推送一條,當js執行完後再請求下一次推送。
function showMsg(msg){ return msg; } function msgctrl::OnMsgNtf(msg) { showMsg() }
javascript 的單線程
JavaScript語言的一大特色就是單線程,也就是說,同一個時間只能作一件事。
JavaScript的單線程,與它的用途有關。做爲瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操做DOM。這決定了它只能是單線程,不然會帶來很複雜的同步問題。好比,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另外一個線程刪除了這個節點,這時瀏覽器應該以哪一個線程爲準?
因此,爲了不復雜性,從一誕生,JavaScript就是單線程,這已經成了這門語言的核心特徵,未來也不會改變。
爲了利用多核CPU的計算能力,HTML5提出Web Worker標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。因此,這個新標準並無改變JavaScript單線程的本質。
Event-Loop(事件循環)
單線程就意味着,全部任務須要排隊,前一個任務結束,纔會執行後一個任務。若是前一個任務耗時很長,後一個任務就不得不一直等着。
若是排隊是由於計算量大,CPU忙不過來,倒也算了,可是不少時候CPU是閒着的,由於IO設備(輸入輸出設備)很慢(好比Ajax操做從網絡讀取數據),不得不等着結果出來,再往下執行。
JavaScript語言的設計者意識到,這時主線程徹底能夠無論IO設備,掛起處於等待中的任務,先運行排在後面的任務。等到IO設備返回告終果,再回過頭,把掛起的任務繼續執行下去。
因而,全部任務能夠分紅兩種,一種是同步任務(synchronous),另外一種是異步任務(asynchronous)。同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。
常見的異步任務有Ajax操做、定時器(setTimeout/setInterval)、UI事件(load(圖片js文件的加載等)、resize、scroll、click等)。網上有文章說定時器是另起一個線程並行執行是不對的,下面是簡單的測試:
setTimeout(function(){console.log(111)},5); console.log(new Date().getTime()) for(var i=0; i<10000000; i++){ } console.log(new Date().getTime()) console.log(777);
運行結果:
能夠看出只有等主線程執行完畢後纔會執行任務隊列中的任務。
具體來講,異步執行的運行機制以下。(同步執行也是如此,由於它能夠被視爲沒有異步任務的異步執行。)
(1)全部同步任務都在主線程上執行,造成一個執行棧(execution context stack)。
(2)主線程以外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
(3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,因而結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步。
下圖就是主線程和任務隊列的示意圖。
只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重複。
參考連接:http://www.ruanyifeng.com/blog/2014/10/event-loop.html