垃圾回收是什么,大家是否是想到垃圾车来收垃圾了?耳边响起东方红?前端
是否是还有人想到想到的是废品回收:收电饭煲、高压窝、煤气灶~~~node
其实今天咱们要讲的是内存中·垃圾回收啦~面试
第一次了解垃圾回收是在一个公众号看到的,当时讲了一下标记清除法和引用计数法,可是当时存在不少疑惑,好比可达不可达究竟是什么?当时也没太在乎。今天从新了解,用本身的话总结分享出来。但愿能给您一些启发和思考。算法
这篇文章包含如下知识点:数组
在文章开始前要知道一个很重要的知识,JS的内存生命周期。今天要讲的就是那些关于内存释放时的故事。浏览器
- 1.为变量分配内存
- 2.使用分配的内存
- 3.不须要的时候将内存释放
首先我认为垃圾回收比较难理解的一个缘由是:它比较抽象,毕竟是浏览器内部的操做,是咱们肉眼不可见的。bash
能够想象咱们平常生活中那些废弃的没用的东西就是“垃圾”,为了占位置就要将生活垃圾清理掉。js内存管理也是如此,只不过他的内存管理是自动执行的,咱们不可见的(可是在没有垃圾回收机制的语言中就须要人为管理内存,好比c语言)。数据结构
知识点1闭包
JavaScript
引擎中有一个后台进程称为垃圾回收器,它监视全部对象,并删除那些不可访问的对象(垃圾)。dom
知识点2
咱们在建立一个字符串、数组等都看做对象(无论是基本类型仍是引用类型)都会为这个对象开辟一个内存空间来保存这个变量。若是访问不到这个对象的时候(没用了)就是垃圾
那么你可能会问, 不可访问的对象是什么呢?怎么知道对象是否能够访问呢?下面就由我一一为你们道来
如何判断垃圾前面说过就是看这个对象可否被访问,那如何知道对象可否被访问?有一个专业的词叫可达性。根据对象是否可达来判断。
JavaScript 中内存管理的主要概念是可达性。
简单地说,“可达性” 值就是那些以某种方式可访问或可用的值,它们被保证存储在内存中。先看一个例子吧
//定义一个user对象,引用name属性
const user={
name:"john"
}
复制代码
这里箭头表示一个对象引用。全局变量user
引用对象 {name:“John”}
,user 的 “name” 属性存储一个基本类型,所以它被绘制在对象中。
若是 user 的值被覆盖,则引用丢失:
例1
//让user的引用为空
user=null
复制代码
图中的箭头表示引用,第一个图user
引用name
属性,第二图让user
指向空,箭头消失,user
没法引用name
属性,js引擎将 {name:“John”}
回收到垃圾桶处理掉,释放了内存空间。
圈个重点👇
当
user
能够访问到name
属性,那name
是可达的;没法访问那么name就是不可达的。
看了上面这个例子不知道对可达性是否有基础的认识了呢?接着咱们继续深刻可达性。
有一组基本的固有可达值,因为显而易见的缘由没法删除, 例如:
本地函数的局部变量和参数
当前嵌套调用链上的其余函数的变量和参数
全局变量
还有一些其余的,内部的
上面这些值称为根。
如今咱们又多了一个概念那就是根
。接着来看一个例子。
例2
// user具备对象的引用
let user = {
name: "John"
};
let admin = user;
复制代码
admin=null;
复制代码
结果是name属性仍是可达的,为何呢?不是已经删除了admin对name的引用吗?
缘由是:虽然admin没有办法引用name,可是user仍是能够引用name属性的,所以能够从根访问到name属性,所以他仍是可达的。
若是再让user=null;
那name才会变成不可达。这个时候没法从根引用name属性了。
上面的图片都来自 这里。咱们继续来看看垃圾回收算法有哪些。
这里主要介绍两种主要回收算法,若是想了解更多,好比标记压缩,GC复制等能够点击这里
引用计数法也很好理解,就是引用对引用的次数进行计数。若是引用了增长就加1,引用减小就减去1.当引用等于0将它清除。看一个例子
//假若有一个计数器count=0
let a ={
name:'linglong',//count==1
}
let b=a; //count==2
b=null; //count==1
a=null; //count==0,被清除
复制代码
假若有一个引用计数的计数器count,依次进行上面四步操做,对于name的引用从0->1->2->1->0
。最后被回收。
引用计数有一个致命的问题就是循环引用,若是两个对象互相引用,尽管再也不使用可是会进入一个无限循环,垃圾回收器不会对他进行回收。看下面代码
function cycle(){
var o1={};
var o2={};
o1.a=o2;
o2.a=o1;
}
cycle();
复制代码
这个代码中cycle函数执行完后不须要了,因此o1和o2的内存应该被释放,可是他们互相引用致使内存不会被回收,如今通常不会使用这个方法,可是ie9以前仍然还在用。如今用的较多的是后面介绍的标记清除法。
标记清除法分为两大步,先标记而后清除没有被标记的。
标记
(记住)它们。
清除
。例如,对象结构以下:
标记清除法利用到了堆、链表结构 标记阶段:从根集合出发,将全部活动对象及其子对象打上标记 清除阶段:遍历堆,将非活动对象(未打上标记)的链接到空闲链表上
这就是垃圾收集的工做原理。JavaScript引擎应用了许多优化,使其运行得更快,而且不影响执行。
v8堆中对象对象分为两组:新生代和老生代
若是有不少对象,而且咱们试图一次遍历并标记整个对象集,那么可能会花费一些时间,并在执行中会有必定的延迟。所以,引擎试图将垃圾回收分解为多个部分(18年提出)。而后,各个部分分别执行。这须要额外的标记来跟踪变化,这样有不少微小的延迟,而不是很大的延迟。
垃圾回收器只在 CPU 空闲时运行,以减小对执行的可能影响。
因为内存泄露和内存没有被释放有关,因此这里简单介绍下何时会产生内存泄露吧!
知识点1:
什么是内存泄露,对于再也不用到的内存若是没有及时释放就叫内存泄露。
这和泄露有半毛钱关系???
我是这样理解的,咱们把内存比做手内心的沙子,当沙子从手里漏了出去,那么可用的内存就愈来愈少了,这个过程就是内存泄露。
可是内存泄露了仍是要管一下的啦,如何对它负责,请接着往下看
理解了内存泄露的概念后,咱们要知道如下几种状况会致使内存泄露
第一个和第四个很好理解,若是有问题能够评论区找linglong,十分欢迎。主要讲讲二、3两个。
当不须要setInterval或者setTimeout时,定时器没有被clear,定时器的回调函数以及内部依赖的变量都不能被回收,形成内存泄漏。
timeout = setTimeout(() => {
var node = document.getElementById('node');
if(node){
fn();
}
}, 1000);
复制代码
解决方法: 在定时器完成工做的时候,手动清除定时器。timeout=null
<body>
<div id="fa">
<button id="button"></button>
</div>
<script>
let div=document.getElementById('fa');
document.body.removeChild(div); // dom删除了
//div=null //切断div对div的引用
console.log(div);
</script>
</body>
复制代码
结果:
<div id="fa">
<button id="button"></button>
</div>
复制代码
咱们能够看到结果中div并无被删除,这是由于代码中删除的div是dom
树中div,let div=document.getElementById('fa');
这句代码中存在着div对div的引用。
solution:咱们要经过div=null
将两次引用都切掉。
除了上面四种,若是有其余的内存泄露状况欢迎指出,一块儿学习,嘻嘻嘻
内存泄露识别方法的内容来自LinDaiDai_霖呆呆小哥哥的建议,超级nice
的做者😃😉
这里展现performance里面查看的方法。主要步骤以下:
LinDaiDai_霖呆呆
的记录一次定时器及闭包问题形成的内存泄漏一文中有详细讲解命令行可使用 Node 提供的process.memoryUsage方法。玲珑对node.JS不熟悉,在这以前不知道这个方法的。经查阅官方文档后得知:
process 是一个全局变量,即 global全局对象下的一个的属性它用于描述当前Node.js 进程状态的对象。
memoryUsage是process下的一个方法,返回一个对象,描述了 Node 进程所用的内存情况,单位为字节。
接下来一块儿实践一下吧
const fun = () => {
console.log(__filename);//文件所在位置的绝对路径
console.log('下面是内存使用信息:');
console.log(process.memoryUsage());
}
fun();
复制代码
咱们看到process.memoryUsage返回一个对象,包含了 Node 进程的内存占用信息。该对象包含四个字段,单位是字节,含义以下。
rss(resident set size):全部内存占用,包括指令区和堆栈。
heapTotal:"堆"占用的内存,包括用到的和没用到的。
heapUsed:用到的堆的部分。
external: V8 引擎内部的 C++ 对象占用的内存。
复制代码
注意:判断内存泄漏,以heapUsed
字段为准。
最后给你们一个思考题:如何减小内存泄露?能够评论区留言哦
看完了给本身一个大大的赞吧,能够问问本身:
认真看完必定会有收获滴,比心~
玲珑以为若是垃圾回收算法的话能够聊不少. 从内存机制开始讲起,什么是垃圾回收,垃圾回收算法,v8引擎如何回收等,内存泄露,以及ES6中国的Weakset和WeakMap这两个不计入垃圾回收机制的弱引用....
若是我有哪些理解不对的地方还请掘友们指正,若是误导你们就尴尬啦
另外文章封面“除了money
都是垃圾”是一句玩笑话啦,毕竟仍是有不少东东高于毛爷爷的,好比大家的star~
和留言!
再次感谢LinDaiDai_霖呆呆小哥哥的鼓励和帮助!