JavaScript 内存管理

JavaScript自动进行内存分配和释放,通过垃圾回收机制管理内存。内存生命周期包括分配、使用和释放。垃圾回收主要算法有引用计数和标记-擦除,其中引用计数在处理循环引用时存在问题,而标记-擦除算法则能有效解决这个问题。JavaScript中的全局对象被视为根对象,用于垃圾回收的可达性分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导言

像 C 语言这样的低级语言提供了手动内存管理的原语malloc()free()。而 JavaScript 则在对象创建时自动分配内存并在它们不再使用时释放它们(垃圾回收)。这种自动化也是一种困惑的来源,那就是开发人员会错误地认为他们不需要考虑内存管理了。

内存的生命周期

无论什么编程语言,内存的生命周期总是相似的:
1.分配你需要的内存。
2.使用分配的内存(读,写)。
3.当你不再使用时,释放已经分配的内存。

在 JavaScript 中的分配

值初始化

JavaScript 在变量声明的时候就替程序员做好了内存分配的活了。

var n = 123; // allocates memory for a number
var s = 'azerty'; // allocates memory for a string 

var o = {
  a: 1,
  b: null
}; // allocates memory for an object and contained values

// (like object) allocates memory for the array and 
// contained values
var a = [1, null, 'abra']; 

function f(a) {
  return a + 2;
} // allocates a function (which is a callable object)

// function expressions also allocate an object
someElement.addEventListener('click', function() {
  someElement.style.backgroundColor = 'blue';
}, false);

通过函数调用分配内存

var d = new Date(); // allocates a Date object

var e = document.createElement('div'); // allocates a DOM element

var s = 'azerty';
var s2 = s.substr(0, 3); // s2 is a new string
// Since strings are immutable value, 
// JavaScript may decide to not allocate memory, 
// but just store the [0, 3] range.

var a = ['ouais ouais', 'nan nan'];
var a2 = ['generation', 'nan nan'];
var a3 = a.concat(a2); 
// new array with 4 elements being
// the concatenation of a and a2 elements

使用值

通过读和写已经分配的内存来使用值。这可以通过读或写一个变量的值或一个对象属性,或者传递一个参数给函数来实现。

在内存不再使用时释放

大多数的内存管理问题发生在这个阶段。最难得任务是确定什么时候已分配的内存不再需要了。通常需要程序员来决定程序中的那一部分不再需要,并释放它。
高级语言会嵌入一小段垃圾回收的代码,用于追踪内存的分配和使用,以确定这块已分配的内存在什么时候不再需要,并自动释放它。这个过程是一个近似的过程,因为判断某一段已分配的内存是否需要是不可预测的(不能被算法决定)。

垃圾回收

垃圾回收算法就是为了解决已分配内存是否还需要的问题的。这一节将会介绍主要的垃圾回收算法和它们的实现。

引用

垃圾回收算法主要依赖的概念是“引用”。在内存管理的上下文中,当一个对象访问了另一个对象(无论是显式还是隐式)可以称为一个引用。比如,一个 JavaScript 对象引用它的原型(隐式引用)还有它的属性值(显式引用)。

引用计数的垃圾回收

这是最朴素的垃圾回收算法。这个算法将“不再使用的对象”归纳为“没有其它对象引用的对象”。当没有指向一个对象的引用时,就可以考虑回收它了。

var o = { 
  a: {
    b: 2
  }
}; 
// 2 objects are created. One is referenced by the other as one of its properties.
// The other is referenced by virtue of being assigned to the 'o' variable.
// Obviously, none can be garbage-collected


var o2 = o; // the 'o2' variable is the second thing that 
            // has a reference to the object
o = 1;      // now, the object that was originally in 'o' has a unique reference
            // embodied by the 'o2' variable

var oa = o2.a; // reference to 'a' property of the object.
               // This object now has 2 references: one as a property, 
               // the other as the 'oa' variable

o2 = 'yo'; // The object that was originally in 'o' has now zero
           // references to it. It can be garbage-collected.
           // However its 'a' property is still referenced by 
           // the 'oa' variable, so it cannot be freed

oa = null; // The 'a' property of the object originally in o 
           // has zero references to it. It can be garbage collected.

局限:循环引用

引用计数的方法的局限是循环引用。在下面的例子中,两个对象在被创建时互相引用对方,形成了一个闭环。它们将在函数调用后脱离作用域,所以它们已经没用了并且可以释放了。然而,引用计数的方法会考虑这两个对象至少有一个引用,并决定不回收它们。

function f() {
  var o = {};
  var o2 = {};
  o.a = o2; // o references o2
  o2.a = o; // o2 references o

  return 'azerty';
}

f();

真实示例

Internet Explorer 6和7使用引用计数的垃圾回收器来回收 DOM 对象。闭环是一个很普遍的产生内存泄露的问题。

var div;
window.onload = function() {
  div = document.getElementById('myDivElement');
  div.circularReference = div;
  div.lotsOfData = new Array(10000).join('*');
};

如上所示,如果 DOM 元素包含大量数据,被这些数据消耗的内存将不会被释放。

标记-擦除算法

这种算法将“一个不再使用的对象”归纳为“一个不可达的对象”。
算法先假定一系列对象为根对象(在 JavaScript 中,根对象是全局对象)。垃圾回收器会周期性地从这些根对象开始,找到所有根对象引用的对象,然后再找这些对象引用的对象,直到找到所有被引用的对象。垃圾回收器会找到所有可达对象并收集所有不可达对象。
直到2012年,所有的现代浏览器都使用的是标记-擦除算法。所有在这个领域的优化也是基于该算法的,但不变的是垃圾回收的目的,“回收掉那些不再使用的对象”。

循环引用不再是一个问题

在上面的例子中,函数调用返回后,那两个循环引用的对象将不会被任何可达树上的对象引用,即不可达。因此他们会被垃圾回收器回收。

局限:对象必须显式不可达

即使是一种局限,但在实际的生产活动中很难遇到,所以人们也不太在意垃圾回收的这点局限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值