垃圾回收
javascript 是使用垃圾回收的语言,也就是说执行环境负责在代码执行时管理内存。
通过自动内存管理实现内存分配和闲置资源回收。
基本思路:确定哪个变量不会再使用,然后释放它占用的内存。
垃圾回收的过程是周期性的。
垃圾回收程序必须跟踪记录哪个变量还会使用,哪个变量不会再使用,以便回收内存。
浏览器发展史上使用过两种标记策略
- 标记清理
- 引用计数
标记清理
javascript 常用的垃圾回收策略。
如:函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记。当变量离开上下文时(函数结束时),也会被加上离开上下文的标记。
🌰垃圾回收程序运行
function getA(){
const a = 2;
const b = 3;
const c = a + b;
const obj = {};
return function(){
return a;
}
}
const f = getA();
- 标记内存中所有变量
- 去除所有 在上下文中的变量以及变量引用到的变量 的标记
- 还有标记的变量就是待删除的
- 内存清理
引用计数
思路:对每个值都记录它被引用的次数。
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