深入理解 JavaScript 垃圾回收机制与内存管理

作者:代码拾荒时间:2026-10-25 19:02:15浏览:470

本文深入解析 JavaScript 的垃圾回收(GC)机制,探讨其如何通过判断对象引用关系来释放内存。文章通过具体代码示例,分析了局部变量与全局变量在 GC 回收过程中的不同表现,并引入作用域链、活动对象及双向链表概念,解释为何局部变量有时无法被立即回收。同时,通过猫与主人的案例演示了因作用域链导致的内存泄漏问题,帮助开发者理解如何有效控制对象生命周期以避免内存浪费。

1.唠叨

JavaScript 垃圾回收机制概述

JavaScript 是一门兼具脚本语言灵活性与高级语言特性的优秀脚本语言。它支持对象的构建与实例化,并内置了垃圾回收机制(GC: Garbage Collection)。通常情况下,开发者使用 new 关键字创建对象,而 GC 则负责回收这些对象所占用的内存空间。因此,深入理解 GC 的工作原理,对于掌握 JavaScript 的内存管理至关重要。


2.用局部变量和全局变量解释GC

GC 在回收内存时,首要任务是判断目标对象是否仍被其他对象引用。只有当确定没有任何对象引用该对象时,GC 才会释放其占用的内存区域。因此,准确判断对象是否不再被引用,是 GC 机制的核心所在。


具体代码实现如下:

 
<script>
function aa(){
this.rr = "弹窗";
} 
function bb(){
this.rr = "弹窗";
} 
var b1;
function cc(){
var a1 = new aa();
b1 = new bb();
return b1;
} 
cc();
alert(b1.rr)
</script>

局部变量与 GC 回收的误区

在上述代码中,执行完 cc() 函数后,局部变量 a1 被回收,随后我们可以通过 b1.rr 弹出文字窗口。许多基础书籍将这种现象解释为:a1 是局部变量,执行完毕后被 GC 回收;而 b1 是全局变量,因此得以保留。然而,这种解释并不全面。请看以下代码示例:


代码如下:

 
<script>
function aa(){
this.rr = "弹窗";
} 
function bb(){
this.rr = "弹窗";
} 
function cc(){
var a1 = new aa();
var b1 = new bb();
return b1;
} 
var b1 = cc();
alert(b1.rr);
</script>

在此例中,cc 函数内部的 a1 和 b1 均为局部变量,但最终仍然成功弹出了文字窗口。这说明 b1 并未被 GC 回收。由此可见,JavaScript 中的局部变量并非在所有情况下都会被 GC 立即回收。


3.抽象理解GC

深入理解 GC:作用域链与活动对象

为了更深入地理解 GC 回收机制,我们需要引入几个关键概念:双向链表、作用域链以及活动对象(为便于理解,此处对原文概念进行了简化,参考链接:[http://softbbs.pconline.com.cn/9497825.html](http://softbbs.pconline.com.cn/9497825.html))。其中,双向链表用于描述复杂对象之间的上下层级关系,而作用域链与活动对象则是双向链表中的具体节点。以函数 cc 为例,其变量层级关系可表示为:


window<=>cc<=>a1<=>rr


<=>b1<=>rr


(原文对此有详细解释)在执行 cc() 方法时,内存中变量的引用关系如上图所示。具体文字解释如下:


window 的活动对象包含 cc,假设 window 是顶级对象(因为在运行过程中不会被回收)。


cc 的活动对象包含 a1 和 b1,其作用域链指向 window。


a1 的活动对象包含 rr,其作用域链指向 cc。


b1 的活动对象包含 rr,其作用域链指向 cc。


当执行 cc() 时,cc 的执行环境会创建一个活动对象和一个作用域链。此时,局部变量 a1 和 b1 都会挂载在 cc 的活动对象中。当 cc() 执行完毕后,执行环境会尝试回收活动对象占用的内存。然而,由于局部变量 b1 通过 return b1 语句,为其增加了一条新的作用域链:window<=>b1<=>rr,因此 GC 停止了对 b1 的回收。


因此,如果希望将一个局部变量或函数提升为全局可用,只需为其增加一条作用域链即可。


作用域链导致的内存泄漏案例

同时,控制好对象的作用域链也变得尤为重要,因为不当的作用域链可能导致 GC 无法回收目标对象,从而引发内存泄漏。例如:


代码如下:

 
<SCRIPT LANGUAGE="JavaScript">
<!-- 
//猫
function cat(name){
var zhuren ;
this.name = name;
//设置主人
this.addZhuRen = function(zr){
zhuren = zr;
} 
this.getZhuRen = function(){
return zhuren;
} 
} 
//主人
function zhuren(name){
this.name = name;
} 
//创建主人:
var zr = new zhuren("zhangsan");
//创建猫
var cat1 = new cat("asan");
//设置该猫的主人
cat1.addZhuRen(zr);
//释放主人
zr = null ;
//此处还存在对主人对象的引用
alert(cat1.getZhuRen().name)
//--> 
</SCRIPT>
作者最新文章