記憶體流失(Memory Leak)的原因是在程式撰寫上的常見錯誤,當使用無內建垃圾回收機制的語言,更容易因為疏忽造成記憶體無法釋放被再次使用的結果,最終讓程式效能因使用時間拉長而變差。
Memory Leak 原因
記憶體流失 (Memory Leak) 發生的原因,依照程式語言的回收機制可分成兩種:
- 沒有回收機制: C 或是 C++ 在存取與 process 獨立的資源後要記得手動釋放
- 有回收機制: 即使像 Java 或 JavaScript 這類有回收機制的語言,仍有可能因撰寫疏忽造成記憶體無法被自動回收,常見記憶體流失(Memory leak)的情境如下:
- 循環參照
- 事件循環監聽
- 存取全域變數
循環參照
同時太多地方去存取同個資源,對自動回收機制來說,就無法確定資源什麼時候沒有被使用。舉例生活上的例子來說:
- A 需要今天下午抄 B 的答案才能交作業
- B 需要今天下午抄 A 的答案才能交作業
Memory Leak 工具推薦
這邊推薦一個方便的套件叫做 madge
可以幫我們產生圖像化的參照圖。
1 | const madge = require("madge"); |
紅色就代表有循環參照 (circular dependencies) 產生
事件循環監聽
在程式的撰寫上,可能因為疏忽就一直增加監聽。舉例生活上的例子來說:
- A 收到問題後,請 B 幫忙查詢問題
- B 收到問題後,請 C 幫忙查詢問題
- C 收到問題後,請 A 幫忙查詢問題
舉 Socket.IO 的例子來說,主要是以下兩個功能:
- 監聽訂閱的訊息
- 針對監聽到的訊息再發送訊息
在這兩個功能互動的過程中,如果需要和 React.js 搭配使用,就要注意 Rerender 時:
- 是否重複產生新的 socket
- 是否重複監聽事件
存取全域變數
避免 closure 去存取到全域變數,像是 setInterval 遇上 closure。
1 | function foo(arg) { |
Memory Leak 解決
- 使用
delete
和將變數設為null
,手動告訴機器這個物件沒有使用了
1 | var myVar = "Hello"; |
- 利用開發者工具中的快照,簡單用法就是使用一陣子之後重新抓一次快照,觀察記憶體有沒有上升太多
- 事件中的 listener 可以放個
console.log('避免重複監聽')
來暴力觀察
參考連結:
- https://developers.google.com/web/tools/chrome-devtools/memory-problems/?hl=zh-tw
- https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Memory_Management
喜歡這篇文章,請幫忙拍拍手喔 🤣