javascript內存管理(堆和棧)和javascript運行機制

內存基本概念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

相關文章
相關標籤/搜索