淺入淺出JS垃圾回識訓制
前置概念
再講垃圾回識訓制之前需要明白兩個知識點:
- 原始值和參考值
ECMASCRIPT變數可以包含兩種不同的型別資料:原始值和參考值,
原始值就是最簡單的資料,有六種:Undefined、Null、Boolean、Number、String和Symbol,
保存原始值得變數是按值訪問的,因為我們操作的就是存盤在變數中的實際值,原始值保存在堆記憶體上,
參考值是保存在記憶體中的物件,JS不允許直接訪問記憶體的位置,因此就不能直接操作物件所在的記憶體空間,在操作物件時實際上是操作對該物件的參考而非實際的物件本身, - 執行背景關系
變數和函式的背景關系決定了它們可以訪問哪些資料,以及它們的行為,每個背景關系都會關聯一個變數物件,而這個背景關系中定義的所有變數和函式都存在于這個物件上,雖然無法通過代碼訪問,但后臺處理時會用到,
全域背景關系是最外層的背景關系,在瀏覽器中全域背景關系就是我們常說的window物件,
每個函式都有自己的背景關系,當代碼執行流進入函式后,函式的背景關系會被推到一個背景關系堆疊上,在函式執行完畢之后,背景關系堆疊就會彈出該函式的背景關系,將控制權返還給之前的執行背景關系,ECMASCRIPT程式的執行流就是通過背景關系堆疊進行控制的,
背景關系中的代碼執行的時候,會創建變數物件的一個作用域鏈,這個作用域鏈決定了各級背景關系中的代碼在訪問變數和函式時的順序,當前在執行的背景關系的變數物件始終位于作用域鏈的最前端,全域背景關系的變數物件始終是作用域鏈的最后一個變數物件,
代碼執行時的識別符號決議是通過沿作用域鏈逐級搜索識別符號名稱完成的,搜索程序始終從作用域鏈的最前端開始,然后逐級往后,直到找到識別符號,(如果沒有找到識別符號,那么通常會報錯,)
垃圾回收
JS是使用垃圾回收的語言,也就是說執行環境負責在代碼執行時管理記憶體,基本思路很簡單:確定哪個變數不會再使用,然后釋放它所占用的記憶體,這個程序是周期性的,垃圾回收程式每隔一段時間就會自動執行,如何標記不在使用的變數也有許多不同的實作方式,在瀏覽器發展歷史上,用到過兩種主要的標記策略:標記清理和參考計數,
標記清理
當變數進入和離開背景關系時,都會被加上相應的標記,隨后垃圾回收程式做一次記憶體清理,銷毀帶標記的所有值并識訓它們的記憶體,給變數加標記的方式有很多,但是標記的程序其實不重要,關鍵是垃圾回收的策略,
參考計數
參考計數的思路就是,宣告一個變數并給它賦一個參考值,這樣它的參考數就為1,當被賦值的變數又被賦值給另一個變數參考數又會加1,當參考的值被覆寫,參考數就會減1,當參考數為0,就可以對該變數進行回收操作,但是該標記策略,當變數回圈參考就會導致參考數一直為2,導致大量記憶體不被釋放,此外該策略也有其他很問題,最終被淘汰,
性能
垃圾回收的時機很重要,如果頻繁的觸發垃圾回收程式,反倒會增加開銷,
開發人員注意事項
在使用垃圾回收的編程環境中,開發者通常無需關心記憶體管理,將記憶體占用量保持在一個較小的值可以讓性能更好,故總結了以下幾點:
-
解除參考
在超出函式背景關系時,參考值會被自動解除,然而對于全域背景關系中的參考值,我們可以顯示的把值置為null,然后他就可以在下次垃圾回收時被回收, -
使用let和const代替var
這兩個關鍵字不僅能改善代碼風格,此外這兩個關鍵字都是塊級作用域,可以讓垃圾回收程式更早介入, -
隱藏類和洗掉操作
如果你的代碼非常注重性能,這點可能對你有所幫助,在Chrome瀏覽器中,使用了V8 JavaScript引擎,在運行期間,V8會將創建的物件域隱藏類關聯起來,以跟蹤他們的屬性特征,能夠共用一個隱藏類的物件性能會更好,在我們對實體進行洗掉或者添加屬性時,此時就回去關聯不同的隱藏類,
function Person(){
this.name="Mercy";
this.phoneNumber="12312341234";
}
let a=new Person();
let b=new Person();//此時a、b兩個實體關聯同一個隱藏類
a.age=22;
delete a.phoneNumber;//在這兩種情況下,兩個個實體就會關聯不同隱藏類;
最佳實踐就是把不想要的屬性置為null,盡量避免新增屬性,做到“先建再補充”,
-
避免記憶體泄漏
這里主要注意兩點,及時定時器的清理和意外宣告全域變數, -
靜態分配(極端形式)
當我們創建陣列時,可以給陣列分配足夠的大小,因為js的陣列大小是動態可變的,當陣列大小不夠時,js引擎會洗掉原有陣列,再新建一個陣列,然后就可能會導致垃圾回收提前,在初期開發中不需要考慮靜態分配,這就屬于過早優化,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/538766.html
標籤:其他
