如何避免前端系統的記憶體洩漏 (memory leak)?

2024年9月26日

💎 加入 E+ 成長計畫 與超過 400+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

在開發軟體時,不論是什麼系統,都會有記憶體體有限的狀況,而前端應用的開發也不例外。從前端的角度來看,我們在程式運行時,每新增一個變數、每呼叫一個函式,都會需要佔用記憶體的空間。

然而,記憶體是有限的。換句話說,當今天正在跑的程式需要更多記憶體,但是執行環境的記憶體不夠,就會出問題。而這種問題又被稱為記憶體洩漏。身為開發者,記憶體洩漏是我們要盡可能去避免的。

記憶體洩漏會怎麼樣?

記憶體洩漏 (memory leak) 是在開發時經常會遇到的問題。從前端的角度看,我們寫的程式會在瀏覽器上運行,而瀏覽器的記憶體是有限的,記憶體洩漏可能會導致瀏覽器沒有記憶體,這會造成兩個嚴重的問題。

第一個是因為記憶體不夠用,瀏覽器會進行垃圾回收 (garbage collection),來釋放更多記憶體;然而,垃圾回收會佔用到線程,太過頻繁將可能導致頁面的性能不佳,可能會出現卡頓的狀況。

第二個是記憶體洩漏過於嚴重,垃圾回收都來不及釋放,這可能讓頁面直接崩潰 (俗稱 memory crash),變得使用者連用都沒辦法用,這會是我們要盡所能去避免的事。

不過讀到這邊你可能會有個疑問,在程式運行時雖然會不斷新增變數,或者有操作來消耗記憶體,但與此同時有些已經不用的東西,除掉不就好? 這樣為什麼要擔心記憶體洩漏呢?

這是個很好的問題,理想的狀況下,不用的記憶體被釋放出來,就能有效避免記憶體洩漏的問題;但偏偏現實世界中,許多開發者會不小心沒做好這件事,導致上述的記憶體洩漏問題。對此,本篇文將會來討論 JavaScript 的記憶體管理,並聊聊如何有效避免記憶體洩漏的問題。

JavaScript 的記憶體管理

在談 JavaScript 的記憶體管理前,我們可以先跳脫這個語言本身,來聊一下程式語言的記憶體管理通常會怎麼做。

一般在寫程式時,與記憶體的互動會有以下步驟

  • 先分配所需要的記憶體
  • 然後使用該記憶體
  • 最後當不再需用該記憶體後,把該記憶體釋放出來
記憶體管理
記憶體管理

在上面的定義之下,如果仍一直有用該記憶體中存的東西,那就沒有問題;有問題的是,當不再需要該記憶體中存的東西時,沒有釋放出來,導致白白佔用空間的狀況。

在許多程式語言中,上述的記憶體管理流程,是需要手動寫在程式中去操作的。然而對前端工程師來說,因為多數時候寫 JavaScript,而 JavaScript 有一個垃圾回收機制,幫助開發者管理記憶體,而不需要手動釋放記憶體。

在 JavaScript 中,垃圾回收算法稱為標記清除 (Mark-and-sweep)

它從根(即全局對象)開始,找到所有從根引用的對象,然後找到這些對象引用的所有對象,如此反覆。如果一個對象無法通過這個算法到達,它將被垃圾回收。

一個對象是否仍然可以從根到達,是決定該對象是否會被垃圾回收的關鍵。而記憶體洩漏則發生在你實際上不再使用一個對象,但它仍然是可達的情況下。具體來說,會是當你不再需要某個東西,但對該東西的引用仍存在,所以該東西不會被垃圾回收,進而導致記憶體洩漏。

閱讀更多

如果你想更深入理解前端常見的記憶體洩漏原因、如何排查記憶體洩漏問題,我們在 E+ 有更深入的內容,有興趣的讀者,歡迎加入 E+ 成長計畫。

本文為 E+ 成長計畫的深度內容,截取段落開放免費閱讀。歡迎加入 E+ 成長計畫閱讀完整版本 (點此了解 E+ 的詳細介紹)

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們