time: 2019.01.22
在此之前,我也是认为 javascript 宿主环境会自己实现垃圾回收,开发人员不用去管理内存的释放
目录
为什么需要了解浏览器内存分配呢?做性能优化必备
性能优化:
内存特指渲染器进程,因为我们的dom解析、js执行都是在渲染进程中执行。那么内存分为:
代码空间:存储代码语句
栈空间:存储调用栈,也就是存储的执行上下文对象,保存了变量数据。其中引用类型在这里保存的是一个引用地址值
堆空间:vm js 对象内存,保存引用类型数据的真实值
gc 根:垃圾回收的起始节点,内部由 js 对象实例句柄组成。
从应用角度触发,分为如下2种:
window | Global 对象 |
高级语言的解释器(宿主环境中的)嵌入了垃圾回收器,但是没有办法明确知道什么时候不再用它了,只能做一个近似的判断,所以在大型项目中,如果不主动回收内存,会存在很多内存被浪费了。
在 js 中,内存回收多是指的 vm 内存回收,也就是从 gc 根不能到达的对象需要回收。
那么如果是被普通变量引用,也可以说是被调用栈引用着的,也不会被回收,那么该怎么理解呢?
答:调用栈中的执行上下文对象内存,在当前代码执行完毕之后,内存自然也就被释放了;如果存在闭包对象,那么会保留当前执行上下文,待引用闭包对象的变量释放之后,则释放闭包内存
垃圾回收算法目前有2种:
老生区:用于存放活跃较久的堆数据(在新生区经历2次回收还存活的)、体积大的数据,使用标记清除算法回收垃圾,主要是慢速回收垃圾
新生区:存放体积小的数据,使用 scanverge 回收垃圾,主要是快速回收垃圾
scanverge:新生区由活跃区和空闲区构成,活跃区快满时执行垃圾回收;结束回收之后,则将活跃区数据复制到空闲区,让分散数据连续起来,优化内存分配;活跃区和空闲区对换,循环
标记清除:从 gc 根出发,定时扫描内存中的对象,凡是能够从根部到达的对象,则保留,否则无法触及到的对象则标记为 无法到达的对象
,稍后清除。通常是分阶段逐步回收,因为数据量大会消耗挺长时间来回收垃圾
内存释放:hello = null
特殊:es6 提供了 WeakSet 和 WeakMap,表示对象的弱引用,是不计算入垃圾回收机制的。
开发者使用 memory takeSnapshot 也会触发 gc
gc 优化的本质是减少 gc 任务的执行
可以查看 系统性能衡量及优化-内存泄漏,来判断是否存在内存泄漏
对于持续运行的服务进程,必须及时释放不再用的内存,否则内存占用越来越高,轻则影响系统性能,重则进程崩溃。
内存泄漏:对于不再使用到的内存,没有及时释放。
要知道内存泄漏的原理,就需要熟悉 标记清除
这个垃圾回收算法,总结一下它的特点
6种常见的js内存泄漏
创建了额外的全局变量,没有使用了,垃圾回收器不会主动回收。
通常未定义的变量也叫做全局变量:
function hello() {
world = 'hello world';
}
解决方案:
"use strict"
null
问题:垃圾回收器既然从全局对象出发,这种定义的全局变量也是挂载在全局对象上的,为什么不主动回收呢?
猜想:垃圾回收器从全局对象出发,必须要是显示声明 window.hello
才行
如果使用 setInterval、setTimeout 定时任务,但是最后又不需要了,却没有清除它,则会造成内存泄漏
let hello = document.getElementById('button');
document.body.removeChild(document.getElementById('button'));
此刻 hello 还是保存了这个按钮对象,虽然按钮对象已经不再 dom 树中了。可以通过 memory 快照中 Detached HTMLBUTTONELEMENT 来查看。
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
var unused = function () {
if (originalThing)
console.log("hi");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log(someMessage);
}
};
};
setInterval(replaceThing, 1000);
闭包环境没有被释放:不是所有定义了没有用的变量都会被垃圾回收器回收,如果该变量值是一个函数对象,函数内部引用了其他变量,构成闭包,那么即使该变量没有地方用到,也不会被垃圾回收器回收,会因为引用了其他变量的原因,持久保存在内存中,需要开发者主动释放。
绑定到 document 等全局对象的事件,如果事件具柄内使用了一些引用对象,则可能会存在内存泄漏,因为这些事件如果不清除,则相关的引用对象也不会被释放。
在控制台输出对象、断点调试程序时,相应的内存也不会被释放
mdn 内存管理
github 木易杨 内存机制
github 木易杨 内存泄漏及如何避免
chrome-devtools/memory
深度理解浏览器中的垃圾回收算法