js內(nèi)存管理
為什么前端要學(xué)習(xí)內(nèi)存管理
一個頁面如果不管理好內(nèi)存會導(dǎo)致頁面卡死瀏覽器奔潰等,所以要寫優(yōu)秀流暢的應(yīng)用,良好的用戶體驗必須要控制頁面的內(nèi)存
為了學(xué)習(xí)nodejs,
js數(shù)據(jù)類型與內(nèi)存機制
- js中 的基本數(shù)據(jù)類型
- boolean
- number
- string
- null
- undefined
- Object
- symple
其中object為引用數(shù)據(jù)類型
- js中內(nèi)存
js中每一個數(shù)據(jù)都有固定的地址去存放,根據(jù)類型的不同,存放的方式也不同,
- 簡單數(shù)據(jù)類型存放在棧內(nèi)存中,
- 引用數(shù)據(jù)類型在棧內(nèi)存中存放一個地址,然后其實際值在堆內(nèi)存中
- 簡單數(shù)據(jù)類型的存放方法
var a = true
var b = 123
var c = 'I am a boy'
上面的代碼執(zhí)行時有如下步驟
第一步 a: true 入棧
第二步 b:123入棧
第三步 c:'I am a boy'入棧
等到程序執(zhí)行完成,一次出棧,程序結(jié)束
- 引用數(shù)據(jù)類型
引用數(shù)據(jù)類型的值是存在堆內(nèi)存的
引用數(shù)據(jù)類型在棧內(nèi)會存儲他的地址,這個地址是指向堆內(nèi)存中的某個區(qū)域,這個區(qū)域存貯著引用類型的對象
js中的垃圾回收
js 使用垃圾回收機制,自動回收內(nèi)存,自動內(nèi)存回收會讓開發(fā)者更省心,但是,開發(fā)也就無法對程序更好的優(yōu)化。
我們要寫出更好的代碼,所以就必須去關(guān)注js引擎中內(nèi)存的回收機制
目前的垃圾回收機制有兩種,引用策略
- 引用策略
- 跟蹤每個值被引用的次數(shù),如果一個值的引用次數(shù)是0那么這個值就不會用到了,相應(yīng)的內(nèi)存就可以釋放
- 每次引用加一,被釋放是減一, 這個值的內(nèi)存空間變?yōu)榱銜r就可以將其從內(nèi)存中回收
const obj = { a: 1 } // 引用+1
const obj1 = { a: 1 } // 引用 + 1
const obj = { } // 引用-1
const obj = null // 引用為0
- 申明了一個變量并將一個引用類型的值賦值給這個變量,這個引用類型的值的引用次數(shù)是1
- 同一個值又被賦值給另一個變量,這個引用類型值的引用次數(shù)加一
- 當(dāng)包含這個引用類型值的變量又被賦值成另一個值了,那么這個引用類型值的引用次數(shù)減一
- 當(dāng)引用測試變成0時,說明這個值沒有辦法去訪問了
- 當(dāng)垃圾收集器下一次運行時,它就會釋放引用此時為0 的值所占用的內(nèi)存
引用類型有一個問題:
? 就是當(dāng)前當(dāng)前的類型值被兩個對象互相引用時就會導(dǎo)致當(dāng)前的引用計數(shù)永遠不為0從而導(dǎo)致內(nèi)存的泄露
比如以下代碼:
// 循環(huán)引用
function f() {
var o1 = { a:1 }
var o2 = { a: 2 }
o1.c = o2
o2.c = o1
}
老的瀏覽器引擎是使用引用計數(shù)來進行垃圾回收的,而現(xiàn)代瀏覽器大多是使用標(biāo)記清除來進行垃圾回收。
- 標(biāo)記清除
標(biāo)記清除指的是當(dāng)變量進入環(huán)境時,這個值被標(biāo)記為“進入環(huán)境”;
當(dāng)變量厲害環(huán)境時將其標(biāo)記為“離開環(huán)境”
最后,垃圾收集器完成內(nèi)存清除工作,銷毀哪些帶有標(biāo)記的值,并回收他們占用的內(nèi)存空間。
執(zhí)行環(huán)境
執(zhí)行環(huán)境定義的變量或者函數(shù)有權(quán)訪問的其他數(shù)據(jù),決定了他們各自的行為。每個執(zhí)行環(huán)境都有一個與之關(guān)聯(lián)的變量對象, 環(huán)境中定義的所有變量和函數(shù)都保存在這個對象中。
執(zhí)行環(huán)境分為全局執(zhí)行環(huán)境、局部執(zhí)行環(huán)境
-
全局執(zhí)行環(huán)境
- 最外圍的一個執(zhí)行環(huán)境
- 根據(jù)宿主環(huán)境不同表示執(zhí)行環(huán)境的對象也不一樣。瀏覽器為window nodejs中為global
- 全局變量和函數(shù)都作為window對象的屬性和方法創(chuàng)建的,這個var與let const不同
- 某個嘻嘻那個環(huán)境中的所有代碼執(zhí)行完畢后,該環(huán)境也相應(yīng)的會被銷毀,保存在其中的多有變量和函數(shù)定義也隨之銷毀,這里閉包內(nèi)的除外
-
局部執(zhí)行環(huán)境(環(huán)境棧)
每個函數(shù)都有自己的執(zhí)行環(huán)境,當(dāng)執(zhí)行流進入一個函數(shù)時,函數(shù)的環(huán)境就會被推入一個環(huán)境棧中。
而在函數(shù)執(zhí)行之后,棧將其環(huán)境彈出,吧控制權(quán)返回給之前的執(zhí)行環(huán)境。
示例代碼
funcion foo () {
var a = 10 // 被標(biāo)記進入環(huán)境
var b = 'hello' // 被標(biāo)記進入環(huán)境
}
foo() // 執(zhí)行完畢 a和b被標(biāo)記離開環(huán)境, 內(nèi)存被回收
v8的內(nèi)存管理機制
v8限制內(nèi)存的大小的原因
- v8最初為設(shè)計瀏覽器而設(shè)計,不需要大的內(nèi)存使用場景
- 防止因為垃圾回收導(dǎo)致的線程暫停時間過長
可以避開限制
?
- v8采用的是分代回收的策略,將內(nèi)存分為兩個生代:新生代,老生代
- v8分別對新生代和老生代使用不同的垃圾回收算法來提升垃圾回收的效率
新生代的垃圾回收
- 內(nèi)存分為from和to空間
- from中存放將要被回收的對象
- to空間中任然要使用的對象
- 垃圾收集器執(zhí)行時將to空間與from空間互換
- 然后將to空間的內(nèi)存釋放
在新生代垃圾回收的過程中,當(dāng)一個對象經(jīng)過多次復(fù)制后依然存活,它將會被認為是生命周期較長的對象,隨后會被移動到老生代中使用新的算法進行管理
在from空南京和to空間進行反轉(zhuǎn)的過程中,如果to空間中的使用量已經(jīng)超過25%,那么就將from中對象直接晉升到老生代的內(nèi)存中去。
老生代內(nèi)存
- 老生代內(nèi)存是一個連續(xù)的空間
- 老生代在垃圾回收的過程中有兩個方法, 標(biāo)記清除,標(biāo)記合并
- 標(biāo)記清除是將不需要的內(nèi)存進行標(biāo)記,在垃圾收集器運行時候講標(biāo)記過的內(nèi)存進行釋放,所以會產(chǎn)生內(nèi)存碎片
- 標(biāo)記合并方法將不需要回收的內(nèi)存挪到一遍,將需要回收的挪到另一邊, 然后對需要回收的內(nèi)存進行整體回收。
如需了解更多
參考文章