一文彻底搞懂js垃圾回收和内存泄露

var obj = {name: ‘hanzichi’, age: 10};

}

function fn2() {

var obj = {name:‘hanzichi’, age: 10};

return obj;

}

var a = fn1();

var b = fn2();

分析:

我们来看代码是如何执行的。首先声明了两个函数,分别叫做 fn1 和 fn2,当 fn1 被调用时,进入 fn1 的环境,会开辟一块内存存放对象{name: 'hanzichi', age: 10},而当调用结束后,出了fn1的环境,那么该块内存会被 JS 引擎中的垃圾回收器自动释放;在 fn2 被调用的过程中,返回的对象被全局变量 b 所指向,所以该块内存并不会被释放。

到底哪个变量是没有用的?

垃圾收集器必须跟踪到底哪个变量没用,对于不再有用的变量打上标记,以备将来收回其内存。通常情况下有两种实现方式:标记清除和引用计数。引用计数不太常用,标记清除较为常用。

标记清除 (因为常用,所以先介绍)


js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。

这段需要背,面试会问到的

function test(){

var a = 10 ; // 被标记 ,进入环境

var b = 20 ; // 被标记 ,进入环境

}

test(); // 执行完毕 之后 a、b又被标离开环境,被回收。

垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

到目前为止,IE9+、Firefox、Opera、Chrome、Safari 的 JS 实现使用的都是标记清除的垃圾回收策略,只不过垃圾收集的时间间隔互不相同。

引用计数


引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为 0 的值所占用的内存。

这段需要背,面试会问到的

function test() {

var a = {}; // a指向对象的引用次数为1

var b = a; // a指向对象的引用次数加1,为2

var c = a; // a指向对象的引用次数再加1,为3

var b = {}; // a指向对象的引用次数减1,为2

}

什么是内存泄漏?


程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。

对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。

在这里插入图片描述

不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。

面试要说的

Chrome 浏览器查看内存泄漏


如果连续五次垃圾回收之后,内存占用一次比一次大,就有内存泄漏。这就要求实时查看内存占用。

Chrome自带的内存调试工具可以很方便地查看内存使用情况和内存泄露:

在 Timeline -> Memory 点击record即可:

在这里插入图片描述

  • 打开开发者工具,选择 Timeline 面板

  • 在顶部的Capture字段里面勾选 Memory

  • 点击左上角的录制按钮。

  • 在页面上进行各种操作,模拟用户的使用情况。

  • 一段时间后,点击对话框的 stop 按钮,面板上就会显示这段时间的内存占用情况。

Vue 中的内存泄漏问题


  1. 如果在mounted/created钩子中使用 JS 绑定了DOM/BOM对象中的事件,需要在 beforeDestroy中做对应解绑处理;

  2. 如果在 mounted/created钩子中使用了第三方库初始化,需要在 beforeDestroy中做对应销毁处理(一般用不到,因为很多时候都是直接全局 Vue.use);

  3. 如果组件中使用了 setInterval,需要在 beforeDestroy中做对应销毁处理;

mounted() {

const box = document.getElementById(‘time-line’)

this.width = box.offsetWidth

this.resizefun = () => {

this.width = box.offsetWidth

}

window.addEventListener(‘resize’, this.resizefun)

},

beforeDestroy() {

window.removeEventListener(‘resize’, this.resizefun)

this.resizefun = null

}

js中的内存泄漏


1. 循环引用

一个很简单的例子:一个DOM对象被一个Javascript对象引用,与此同时又引用同一个或其它的Javascript对象,这个DOM对象可能会引发内存泄露。这个DOM对象的引用将不会在脚本停止的时候被垃圾回收器回收。要想破坏循环引用,引用DOM元素的对象或DOM对象的引用需要被赋值为null。

2. 闭包

在闭包中引入闭包外部的变量时,当闭包结束时此对象无法被垃圾回收(GC)。

var a = function() {

var largeStr = new Array(1000000).join(‘x’);

return function() {

return largeStr;

}

}();

3. DOM泄露

当原有的COM被移除时,子结点引用没有被移除则无法回收。

var select = document.querySelector;

var treeRef = select(‘#tree’);

//在COM树中leafRef是treeFre的一个子结点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值