性能優(yōu)化主要相關(guān)模塊
- 內(nèi)存管理
- 垃圾回收與常見GC算法
- V8引擎的垃圾回收
- Performance工具
- 代碼優(yōu)化實例
1. 內(nèi)存管理
JavaScript 內(nèi)存管理 Memory Management
? 內(nèi)存為什么需要管理
function fn() {
arrList = []
arrList[100000] = 'lg is a coder'
}
fn()
內(nèi)存管理介紹
- 內(nèi)存: 由可讀寫單元組成徙瓶,表示一片可操作空間
- 管理:人為的去操作一片空間的申請、使用和釋放
- 內(nèi)存管理:開發(fā)者主動申請空間集侯、使用空間新症、釋放空間
- 管理流程: 申請-使用-釋放
JavaScript 中的內(nèi)存管理
- 申請內(nèi)存空間
- 使用內(nèi)存空間
- 釋放內(nèi)存空間
// 申請
let obj = {}
// 使用
obj.name = 'lg'
// 釋放
obj = null
2. 垃圾回收與常見GC算法
2.1 垃圾回收
JavaScript 中的垃圾
- JavaScript中的內(nèi)存管理是自動的
- 對象不再被引用時是垃圾
- 對象不能從根上訪問到時 是垃圾
JavaScript 中的可達對象
- 可以訪問到的對象就是可達對象 (引用、作用域鏈)
- 可達的標(biāo)準就是從根出發(fā)是否能夠被找到
- JavaScript 中的根就可以理解為是全局變量對象
JavaScript 中的引用和可達
let obj = {name: 'xm'} // 聲明了一個xm的內(nèi)存空間
let ali = obj // ali也引用了這個空間
obj = null // obj斷開與xm的內(nèi)存空間連接媚创,但是ali還引用著
2.2 GC 算法介紹
- GC 就是垃圾回收機制的簡寫
- GC 可以找到內(nèi)存中的垃圾昧互、并釋放和回收空間
GC中的垃圾是什么
- 程序中不再需要使用的對象
function func() {
name = 'lg'
return `${name} is a coder`
}
func()
- 程序中不能再訪問到的對象
function func() {
const name = 'lg'
return `${name} is a coder`
}
func()
GC 算法是什么
- GC 是一種機制筋蓖,垃圾回收器完成具體的工作
- 工作的內(nèi)容就是查找垃圾釋放空間荤西、回收空間
- 算法就是工作時查找和回收所遵循的規(guī)則
常見GC算法
- 引用計數(shù)
- 標(biāo)記清除
- 標(biāo)記整理
- 分代回收
2.3 引用計數(shù)澜搅、標(biāo)記清除、標(biāo)記整理
1. 引用計數(shù)
引用計數(shù)算法實現(xiàn)原理
- 核心思想: 設(shè)置引入數(shù)邪锌,判斷當(dāng)前引用數(shù)是否為0
- 引用計數(shù)器
- 引用關(guān)系改變時修改引用數(shù)字
- 引用數(shù)字為0時立即回收
引用計數(shù)算法優(yōu)點
- 發(fā)現(xiàn)垃圾時立即回收
- 最大限度減少程序暫停
引用計數(shù)算法缺點
- 無法回收循環(huán)引用的對象
- 時間開銷大
2.標(biāo)記清除
標(biāo)記清除算法實現(xiàn)原理
- 核心思想:分標(biāo)記和清除二個階段完成
- 遍歷所有對象找標(biāo)記活動對象
- 遍歷所有對象清除沒有標(biāo)記對象
(注意:在第二階段當(dāng)中勉躺,會把第一個階段所設(shè)置的標(biāo)記給抹掉,便于我們GC下次能正常工作)
- 回收相應(yīng)的空間
標(biāo)記清除算法圖示
會把回收的空間直接放在一個叫做空閑列表上面觅丰,方便我們后續(xù)的程序可以直接在這兒可以申請使用
標(biāo)記清除算法優(yōu)點
*. 標(biāo)記清除可以解決循環(huán)引用問題
標(biāo)記清除算法缺點
- 空間的碎片化
由于當(dāng)前所回收的垃圾對象在地址上它本身是不連續(xù)的饵溅,從而造成我們回收之后它們分散在各個角落,后續(xù)如果我們需要使用妇萄,剛好巧了概说,新的生成空間剛好與之匹配那可以直接用,一但多了或是少了就不太適合使用了嚣伐。
// 任何的空間都會有兩個部分
// 1. 頭(存儲這個空間的元信息 eg: 大小,地址 --- 稱為頭)
// 2. 域 (存放數(shù)據(jù)的)
// 如下圖 假設(shè)一個方塊是1個內(nèi)存空間 左邊藍色的釋放后得2個空間萍丐、右邊藍色的釋放后得1個空間轩端,在釋放完成后,其實它們是分散的逝变,也就是地址不連續(xù)基茵。
// 地址不連續(xù)
// 如果下一次需要申請1.5的空間,左邊多了壳影,右邊少了拱层。 最大問題-空間碎片化
缺點圖示
總結(jié):相對引用計數(shù)來說,解決了循環(huán)引用問題宴咧,缺點則是造成空間碎片化根灯,不能讓空間得到最大化的使用
3. 標(biāo)記整理
標(biāo)記整理算法實現(xiàn)原理
- 標(biāo)記整理可以看做是標(biāo)記清除的增強
- 標(biāo)記階段的操作和標(biāo)記清除一致
- 清除階段會先執(zhí)行整理,移動對象位置 -- 保證地址連續(xù)性
標(biāo)記整理算法圖示
2.2 GC算法總結(jié)
引用計數(shù)優(yōu)缺點
- 優(yōu)點:
- 可以即時回收垃圾對象
- 減少程序卡頓時間
- 缺點
- 無法回收循環(huán)引用的對象
- 資源消耗較大s
標(biāo)記清除
- 優(yōu)點:
- 可以回收循環(huán)引用的對象
- 缺點
- 空間產(chǎn)生碎片化空間,浪費空間
標(biāo)記整理
- 優(yōu)點:
- 減少碎片化空間
- 缺點:
- 不會立即回收垃圾對象
3. V8引擎
3.1 認識V8
- V8是一款主流的 JavaScript 執(zhí)行引擎
- V8采用即時編譯
- V8內(nèi)存設(shè)限
即時編譯: 之前很多的JavaScript引擎都需要將源代碼轉(zhuǎn)成字節(jié)碼烙肺,然后再去執(zhí)行纳猪。而對于V8來說,就可以直接將源碼翻譯成我們當(dāng)前可以直接執(zhí)行的機器碼桃笙,所以這個時候的速度是非呈系蹋快。
內(nèi)存設(shè)限: 64位的操作系統(tǒng)下搏明,上限是不超過1.5G鼠锈,對于32位操作系統(tǒng),上限是不超過800M星著。
?為什么有內(nèi)存設(shè)限
- V8本身就是為了瀏覽器而制造的购笆,現(xiàn)有的內(nèi)存大小對于網(wǎng)頁來說是足夠使用了
- V8內(nèi)部所去實現(xiàn)的垃圾回收機制,采用這個機制是合理的强饮。
官方做個測試
當(dāng)我們的垃圾內(nèi)存達到1.5個G的時候由桌,如果V8采用增量標(biāo)記的算法進行垃圾回收,只需要消耗50毫秒邮丰,而如果采用非增量標(biāo)記的算法進行回收行您,則需要1秒鐘。那么從用戶體驗角度來說剪廉,1秒鐘算很長的時間了娃循,在這里就以1.5個G來為界了。
3.2 V8垃圾回收
V8垃圾回收策略
- 采用分代回收的思想
- 內(nèi)存分為新生代斗蒋、老生代
- 針對不同對象采用不同算法
V8垃圾回收策略圖示
V8中常用GC算法
- 分代回收
- 空間復(fù)制
- 標(biāo)記清除
- 標(biāo)記整理
- 標(biāo)記增量
V8內(nèi)存分配
- V8內(nèi)存空間一分為二
- 小空間用于存儲新生代對象(32M | 16M)
- 新生代指的是存活時間較短的對象
什么叫存活時間較短 eg:局部的作用域捌斧,這個局部的作用域當(dāng)中的變量在執(zhí)行完成之后就肯定要去回收
如何回收新生代對象
新生代對象回收實現(xiàn)
- 回收過程采用復(fù)制算法 + 標(biāo)記整理
- 新生代內(nèi)存區(qū)分為二個等大小空間 (FROM、TO)
- 使用空間為From泉沾,空閑空間為To
- 活動對象存儲于From空間
- 標(biāo)記整理后將活動對象拷貝至To
- From與To交換空間完成釋放
回收細節(jié)說明
- 拷貝過程中可能出現(xiàn)晉升
- 晉升就是將新生代對象移動至老生代
- 一輪GC還存活的新生代需要晉升
- To空間的使用率超過了25%
為什么是25%捞蚂?
因為我們在將來進行回收操作的時候,最終是要吧From的空間和To的空間進行一個交換跷究,也就是說以前的To會變成From姓迅,而From會變成To,也就意味著如果To使用率達到了80%俊马,那么最終它變成活動對象存儲空間后丁存,那么新的對象好像就存不進去了
如何回收老生代對象
老生代對象說明
- 老生代對象存放在右側(cè)老生代區(qū)域
- 64位操作系統(tǒng)1.4G,32操作系統(tǒng)700M
- 老生代對象就是指存活時間較長的對象
老生代對象回收實現(xiàn)
- 主要采用標(biāo)記清除柴我、標(biāo)記整理增量標(biāo)記算法
- 首先使用標(biāo)記清除完成垃圾空間的回收
- 采用標(biāo)記整理進行空間優(yōu)化
- 采用增量標(biāo)記進行效率優(yōu)化
什么時候進行標(biāo)記整理?
當(dāng)把我們新生代區(qū)域里面的內(nèi)容往我們當(dāng)前這個老生代區(qū)域進行移動時解寝,而且在這個時間節(jié)點上老生代區(qū)域的空間又不足以來存放我們新生代存儲區(qū)所移過來的對象
<font color="red">細節(jié)對比</font>
- 新生代區(qū)域垃圾回收使用空間換時間
- 老生代區(qū)域垃圾回收不適合復(fù)制算法
標(biāo)記增量如何優(yōu)化垃圾回收
<font color="red">增量標(biāo)記算法:</font>將一整段的垃圾回收操作,拆分成多個小步艘儒,組合完成整個垃圾回收操作聋伦。我們知道夫偶,當(dāng)垃圾回收工作的時候,會阻塞JS程序執(zhí)行嘉抓,當(dāng)我們需要優(yōu)化垃圾回收的效率時索守,就可以使用增量標(biāo)記算法。
<font color="red">優(yōu)點:</font>讓垃圾回收與程序執(zhí)行可以交替完成抑片,讓時間消耗更合理卵佛,達到效率優(yōu)化的好處。
工作原理:
- JS 程序執(zhí)行的過程中敞斋,會伴隨著垃圾回收的工作
- 當(dāng)垃圾回收工作時截汪,需要遍歷對象進行標(biāo)記,此時不需要將所有對象進行標(biāo)記植捎,可以先將直接可達的對象進行標(biāo)記衙解,此時停下標(biāo)記操作
- 然后讓JS程序執(zhí)行一會,之后焰枢,再讓 GC 機制去做二步的標(biāo)記操作蚓峦,去標(biāo)記那些間接可達的對象
- 重復(fù)以上兩步,讓程序執(zhí)行和垃圾回收的標(biāo)記操作交替執(zhí)行济锄,來達到優(yōu)化效率和提升用戶體驗的目的
- 直到標(biāo)記操作完成之后暑椰,最后執(zhí)行垃圾回收
V8垃圾回收總結(jié)
- V8 是一款主流的JavaScript執(zhí)行引擎
- V8 內(nèi)存設(shè)置上限
- V8 采用基于分代回收思想實現(xiàn)垃圾回收
- V8 內(nèi)存分為新生代和老生代
- V8 垃圾回收常見的GC算法