如何避免前端系统的记忆体泄漏 (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 上追蹤我們