js是動態語言,並沒有提供內存管理相關的的api操作,需要開發人員在寫代碼中規避或者預判處理相關內存問題,隨着前端複雜度逐漸增高,前端開發者對於js內存管理要求逐漸加強,如果你想進階高級那麼必須對內存管理有所較深理解,本篇博文詳細介紹js內存相關知識。
首先從js數據類型着手
js實際上有兩種數據類型:
原始數據類型
字符串String、數字Number、布爾Boolean、數組Array、空對象Null、未定義Undifined、Symbol
引用數據類型
Object,array、function、date
內存空間:棧內存stack和堆內存heap
原始數據類型是保存在棧內存中,引用類型數據是保存在棧和堆內存中。
棧結構先進後出如下圖
js是無法直接操作堆內存,是通過棧內存中引用堆內存地址來實現堆內存的訪問如下圖
當我們聲明一個函數
var fn = function(){
var i=10;
var j=10;
console.log(i+j);
}
時內存的情況是這樣:首先在堆內存中開闢空間存入函數體字符串,然後在棧內存中存入變量fn 其值是堆內存的地址如下圖:
引用類型具有如下幾個特點:
- 引用類型可以添加屬性和方法
- 引用類型的賦值是對象引用
- 引用類型的比較是引用的比較
- 引用類型是同時保存在棧區中和堆區中的,引用類型的存儲需要在內存的棧區和堆區中共同完成,棧區保存變量標識符和指向堆內存的地址。
js的垃圾回收機制
原理
JS的垃圾回收機制是爲了以防內存泄漏,內存泄漏的含義就是當已經不需要某塊內存時這塊內存還存在着,垃圾回收機制就是間歇的不定期的尋找到不再使用的變量,並釋放掉它們所指向的內存。在JS中,JS的執行環境瀏覽器會負責管理代碼執行過程中使用的內存。
js變量的生命週期
當一個變量的生命週期結束之後它所指向的內存就應該被釋放。JS有兩種變量,全局變量和在函數中產生的局部變量。局部變量的生命週期在函數執行過後就結束了,此時便可將它引用的內存釋放(即垃圾回收),但全局變量生命週期會持續到瀏覽器關閉頁面。
瀏覽器如何檢測變量實現垃圾回收
目前瀏覽器有兩種檢測js變量回收垃圾的機制,標記清除和引用計數
引用計數這種方式常常會引起內存泄漏,低版本的IE使用這種方式。機制就是跟蹤一個值的引用次數,當聲明一個變量並將一個引用類型賦值給該變量時該值引用次數加1,當這個變量指向其他一個時該值的引用次數便減一。當該值引用次數爲0時就會被回收。當程序中出現循環引用時該方式會引起內存泄漏。
標記清除大部分瀏覽器以此方式進行垃圾回收,當變量進入執行環境(函數中聲明變量)的時候,垃圾回收器將其標記爲“進入環境”,當變量離開環境的時候(函數執行結束)將其標記爲“離開環境”,在離開環境之後還有的變量則是需要被刪除的變量。標記方式不定,可以是某個特殊位的反轉或維護一個列表等。
垃圾收集器給內存中的所有變量都加上標記,然後去掉環境中的變量以及被環境中的變量引用的變量的標記。在此之後再被加上的標記的變量即爲需要回收的變量,因爲環境中的變量已經無法訪問到這些變量。
全局執行環境:是個相對概念,瀏覽器中最外層環境是window對象node中是global對象,全局變量和全局函數都是在window上,同理某個模塊或方法中的變量及方法都會在該環境執行完成後環境銷銷燬同時對應的變量和方法也銷燬,頂層window對象會在瀏覽器頁面關閉後才銷燬。
局部環境棧:每個函數都有其自己的執行環境,當執行流到來時該函數會進入局部執行棧,執行完成後棧彈出,控制權返回原來(上層)的執行環境。
V8的內存管理機制
Google V8 引擎使用 C++ 代碼編寫,實現了 ECMAScript 規範的第五版,可以運行在所有的主流操作系統中,甚至可以運行在移動終端 ( 基於 ARM 的處理器,如 HTC G7 等 )。V8 最早被開發用以嵌入到 Google 的開源瀏覽器 Chrome 中,但是 V8 是一個可以獨立的模塊,完全可以嵌入您自己的應用,著名的 Node.js( 一個異步的服務器框架,可以在服務端使用 JavaScript 寫出高效的網絡服務器 ) 就是基於 V8 引擎的。
和其他 JavaScript 引擎一樣,V8 會編譯 / 執行 JavaScript 代碼,管理內存,負責垃圾回收,與宿主語言的交互等。V8 的垃圾回收器採用了衆多技術,使得其運行效率大大提高。通過暴露宿主對象 ( 變量,函數等 ) 到 JavaScript,JavaScript 可以訪問宿主環境中的對象,並在腳本中完成對宿主對象的操作。
老生代和新生代垃是v8圾回收最重要的機制。