一樣是今天面是遇到的問題,沒答好,紀錄一下
一、 V8 引擎運作流程:從原始碼到機器碼
V8 引擎(Chrome 與 Node.js 的核心)並非單純的解釋器,它是一個複雜的 JIT (Just-In-Time) 編譯器。
解析 (Parsing):將 JavaScript 原始碼轉化為 AST (抽象語法樹)。
解釋 (Ignition):解釋器將 AST 轉為較低階的 Bytecode (位元組碼) 並立即執行。
編譯優化 (TurboFan):監控程式碼運行(Profiling),如果某段函式被頻繁呼叫(熱點程式碼),編譯器會將其轉換為優化過的 機器碼 (Machine Code),大幅提升速度。
去優化 (Deoptimization):如果輸入的資料型別突然改變(例如從
Int變成String),V8 會拋棄機器碼,退回到位元組碼執行,這就是效能損耗的來源。
二、 V8 垃圾回收機制 (Garbage Collection, GC)
V8 將記憶體分為 新生代 (Young Generation) 與 老生代 (Old Generation),並採用不同的回收策略:
1. 新生代:Scavenge 演算法
特性:存活時間短、頻繁回收。
機制:將空間平分為
From與To。新物件存入From,回收時將存活物件複製到To,然後清空From並角色對調。晉升 (Promotion):經歷兩次回收仍存活的物件,會被移動到老生代。
2. 老生代:Mark-Sweep & Mark-Compact
特性:存活時間長、空間大。
機制:
標記 (Marking):從根節點(如
window或global)出發,標記所有可達(Reachable)的物件。清除 (Sweeping):回收未被標記的記憶體空間。
整理 (Compacting):將分散的物件移動到一塊連續空間,減少「記憶體碎片」。
三、 如何偵測與處理記憶體洩漏 (Memory Leak)
當物件不再被需要,但仍被「根節點」間接引用,導致 GC 無法回收時,就會發生記憶體洩漏。
1. 常見的洩漏場景
全域變數:不小心宣告了
window.data = [...]。未清除的計時器:
setInterval內引用了外部變數,但從未調用clearInterval。閉包 (Closures):內部函式引用了外部大型變數,導致外部變數無法被回收。
脫離文件的 DOM 節點:在 JS 中保留了對 DOM 的引用,即使該 DOM 已從頁面移除。
2. 實戰處理步驟
當你懷疑高併發頁面出現記憶體異常時:
使用 Chrome DevTools (Performance 面板):
勾選「Memory」並錄製一段操作。
觀察 Heap (堆疊) 曲線,若呈現「階梯式上升」且不會回落,即代表洩漏。
堆疊快照 (Heap Snapshot):
進入
Memory面板,點擊Take snapshot。執行可疑操作後,再拍一張快照。
使用 "Comparison" (比較) 模式,找出在兩次快照之間「新增且未消失」的物件。
三點定位法:
拍下快照 A(初始狀態)。
執行操作並拍下快照 B。
再執行操作並拍下快照 C。
檢查 B 與 C 之間持續增長的物件,這通常就是洩漏點。
四、 程式碼優化建議
弱引用 (WeakMap / WeakSet):如果你需要建立物件與資料的關聯,但不希望影響 GC 回收,請優先使用
WeakMap。解構賦值後手動置空:大型物件處理完後,設定
obj = null斷開引用鏈。生命週期管理:在 React 的
useEffect或 Vue 的onUnmounted中,務必移除 Event Listener 或清除 Timer。
沒有留言:
張貼留言