內(nèi)存管理
內(nèi)存管理介紹
- 內(nèi)存:由可讀寫單元組成,表示一片可操作空間
- 管理:認為的去操作一篇空間的申請、使用和釋放
- 內(nèi)存管理:開發(fā)者主動申請空間、使用空間、釋放空間
- 管理流程:申請-使用-釋放
垃圾回收與常見GC算法
JavaScript中的垃圾
- JavaScript中內(nèi)存管理是自動的
- 對象不在被引用時是垃圾
- 對象不能從根上訪問到時時垃圾
JavaScript中的可達對象
- 可以訪問到的對象就是可達對象(引用、作用域鏈)
- 可達的標準就是從根出發(fā)是否能夠被找到
- JavaScript中的根就可以理解為時全局變量對象
GC算法
GC定義與作用
- GC就是垃圾回收機制的簡寫
- GC可以找到內(nèi)存中的垃圾、并釋放和回收空間
GC里的垃圾是什么
- 程序中不在需要使用的對象
- 程序中不能再訪問到的對象
GC算法
- GC是一種機制,垃圾回收器完成具體的工作
- 工作的內(nèi)容就是查找垃圾釋放空間、回收空間
- 算法就是工作時查找和回收所遵守的規(guī)則
常見GC算法
- 引用計數(shù)
- 核心思想:設置引用數(shù),判斷當前引用數(shù)是否為0
- 引用計數(shù)器
- 引用關系改變時修改引用數(shù)字
- 引用數(shù)字為0時立即回收
引用計數(shù)算法優(yōu)缺點- 發(fā)現(xiàn)垃圾時立即回收
- 最大限度減少程序暫停執(zhí)行(程序卡頓時間)
- 無法回收循環(huán)引用的對象
- 時間開銷大(資源消耗大)
- 標記清除
- 核心思想:分標記和清楚兩個階段
- 遍歷所有對象找到標記活動對象
- 遍歷說有對象清除沒有標記對象
- 回收相應的空間
標記清除算法優(yōu)缺點 - 解決了引用計數(shù)中無法回收循環(huán)引用的對象
- 會產(chǎn)生空間碎片化的問題,使得空間不能最大化的使用
- 不會立即回收垃圾對象(在回收垃圾時程序時停止執(zhí)行的)
- 標記整理
- 標記整理可以看作時標記清除的增強
- 標記階段的操作和標記清除一致
- 清除階段會執(zhí)行整理,移動對象的位置,使它們在地址上連續(xù)
標記整理算法優(yōu)缺點 - 減少碎片化空間
- 不會立即回收垃圾對象(在回收垃圾時程序時停止執(zhí)行的)
- 分代回收
V8引擎的垃圾回收
V8是一款主流的JavaScript執(zhí)行引擎,采用即時編譯,內(nèi)存設有上限,采用分代回收思想實現(xiàn)垃圾回收,內(nèi)存分為新生代和老生代
回收策略
- 采用分代回收策略
- 內(nèi)存分為新生代、老生代
- 針對不同的對象采用不同的算法
V8常用GC算法
- 分代回收
- 空間復制
- 標記清除
- 標記整理
- 標記增量
V8如何回收新生代對象
V8內(nèi)存分配
- V8內(nèi)存空間一分為二
- 小空間用于存儲新生代對象(32M | 16M)
- 新生代指的是存貨時間較短的對象
新生代對象回收實現(xiàn)
- 回收過程采用復制算法+標記整理
- 新生代內(nèi)存區(qū)分為兩個等大小空間
- 使用空間為From,空閑空間為To
- 活動對象存儲于From空間
- 標記整理后將活動對象拷貝至To空間
- From與To交換空間完成釋放
回收細節(jié)說明
- 拷貝過程中可能出現(xiàn)晉升
- 晉升就是將新生代對象移動至老生代
- 一輪GC還存貨的新生代需要晉升
- To空間的使用率超過25%
V8如何回收老生代對象
老生代對象說明
- 老年代對象存放在右側老生代區(qū)域
- 64位操作系統(tǒng)1.4G,32位操作系統(tǒng)700M
- 老生代對象就是指存輝時間較長的對象
老生代對象回收實現(xiàn)
- 主要采用標記清除、標記整理、增量標記算
- 首先使用標記清除完成垃圾空間的回收
- 采用標記整理進行空間優(yōu)化(新生代晉升老生代過程中,發(fā)現(xiàn)老生代空間不足時)
- 采用增量標記進行效率優(yōu)化
細節(jié)對比
- 新生代區(qū)域垃圾回收使用空間換時間(使用復制算法)
- 老生代區(qū)域垃圾回收不適合復制算法
Performance工具
為什么使用Performance
- GC的目的是為了實現(xiàn)內(nèi)存空間的良性循環(huán)
- 良性循環(huán)的基石時合理使用
- 時刻關注才能確定是否合理
- Performance提供更多監(jiān)控方式
Performance使用步驟
- 打開瀏覽器輸入目標地址
- 進入開發(fā)人員工具面板,選擇性能(Performance)
- 開啟錄制功能,訪問具體頁面
- 執(zhí)行用戶行為,以斷事件后停止錄制
- 分析界面中記錄的內(nèi)存信息
內(nèi)存問題的外在表現(xiàn)
- 頁面出現(xiàn)延遲或經(jīng)常性暫停(保證網(wǎng)絡正常情況下)
- 頁面持續(xù)性出現(xiàn)糟糕的性能(保證網(wǎng)絡正常情況下)
- 頁面的性能隨時間延長越來越差(出現(xiàn)內(nèi)存泄漏)
監(jiān)控內(nèi)存的幾種方式
界定內(nèi)存問題的標準
- 內(nèi)存泄漏:內(nèi)存使用持續(xù)升高
- 內(nèi)存膨脹:在多數(shù)設備上都存在性能問題
- 頻繁垃圾回收:通過內(nèi)存變化圖進行分析
幾種監(jiān)控方式
- 瀏覽器任務管理器
- TimeLine時序圖記錄
- 堆快照查找分離DOM
- 判斷是否存在頻繁的垃圾回收
代碼優(yōu)化實例
如何和精準測試JavaScript性能
- 本質(zhì)上就是采集大量的執(zhí)行樣本進行數(shù)學統(tǒng)計和分析
- 使用基于Benchmark.js的https://jsperf.com/完成
Jsperf使用流程
- 使用GitHub帳號登錄
- 填寫個人信息(非必填)
- 填寫詳細的測試用例信息(title、slug)
- 填寫準備代碼(DOM操作時經(jīng)常使用)
- 填寫必要的setup與teardown代碼
- 填寫測試代碼片段
為什么要慎用全局變量
- 全局變量定義在全局執(zhí)行上下文,是所有作用域鏈的頂端
- 全局執(zhí)行上下文一直存在與上下文執(zhí)行棧,直到程序退出
- 如果某個局部作用域出在線了同名變量則會遮蔽或污染全局
緩存全局變量
- 將使用中無法避免的全局變量緩存到局部(document緩存)