Javascript 垃圾回收机制

55 阅读3分钟

垃圾回收

javascript 是使用垃圾回收的语言,也就是说执行环境负责在代码执行时管理内存。

通过自动内存管理实现内存分配和闲置资源回收。

基本思路:确定哪个变量不会再使用,然后释放它占用的内存。

垃圾回收的过程是周期性的。

垃圾回收程序必须跟踪记录哪个变量还会使用,哪个变量不会再使用,以便回收内存。

浏览器发展史上使用过两种标记策略

  • 标记清理
  • 引用计数

标记清理

javascript 常用的垃圾回收策略。

如:函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记。当变量离开上下文时(函数结束时),也会被加上离开上下文的标记。

🌰垃圾回收程序运行

function getA(){
	const a = 2;
	const b = 3;
	const c = a + b;
	const obj = {};
	return function(){
		return a;
	}
}

const f = getA();

  1. 标记内存中所有变量
  2. 去除所有 在上下文中的变量以及变量引用到的变量 的标记
  3. 还有标记的变量就是待删除的
  4. 内存清理

引用计数

思路:对每个值都记录它被引用的次数。

function test(){
	let a = {};
	let b = {
		c: a
	};
	b.c = null
	a = null
	b = null
}

垃圾回收程序下次运行时就会是否引用次数为 0 的值的内存

但是循环引用的引用数永远不会变成0,除非手动 设为 null

内存管理

出于安全考虑(避免浏览器占用大量内存,把操作系统搞崩),浏览器内存相对较少。将内存占用量保持再一个较小的值可以让页面性能更出色。

解除引用

执行代码时,只保存必要的数据

如果数据不在必要,把它设置为 null,从而释放其引用

这个建议适合再全局变量和全局对象的属性

局部变量超出作用域后会自动释放

function createPerson(name) {
	let localPerson = new Object();
	localPerson.name = name;
	return localPersom
}

// 函数执行完成, localPerson 自动解除引用
let globalPerson = createPerson("nicholas")

// 收到解除 globalPerson 对值的引用
globalPerson = null;

通过 const let 声明提升性能

const 和 let 以块为作用域,更早的解除引用。

隐藏类

在 v8 中,对象回合隐藏类关联起来,以跟踪它们的属性特征。能共享相同隐藏类的对象性能会更好(这是为什么??仅仅只是复用了吗??)

function Article(){
	this.title = "ttttt"
}

let article1 = new Article();
let article2 = new Article();

这两个实例共享同一个隐藏类,因为有相同的构造函数和原型

article2.author = 'jack'

此时两个实例对应两个不同的隐藏类

避免方案:先创建再补充 动态属性赋值

function Article(author){
	this.title = "ttttt"
	this.author = author
}

let article1 = new Article();
let article2 = new Article('jack');

此时这两个实例基本一样,可以共享一个隐藏类

添加下行代码

delete a1.author

即使两个实例用了同一个构造函数,也不共享一个隐藏类

最佳实践是把不想要的属性设置为 null

a1.author = null

内存泄漏

闭包问题

let out = function(){
	let name = 'jack';
	return function(){
		return name;
	}
}

调用 out 会导致分配给 name 的内存泄露。

只要返回的函数存在,即 out 存在,就不能清理 name