内存管理
内存为什么需要管理呢
例如下图,执行这个函数从语法上来说没有问题,但是可以看到蓝色的内存监控线,持续升高,并且在这个过程中没有看到回落,这说明存在内存泄漏。
如果我们在写代码的过程中不了解一些内存管理的机制,可能会编写一些不容易察觉到的问题代码。
一些概念
我们先回顾一些内存相关的概念:
- 内存:由可读写单元组成,表示一片可操作的空间
- 管理:人为的去操作一片空间的申请、使用和释放
- 内存管理:开发者主动申请空间、使用空间和释放空间
- 管理流程:申请---使用---释放
在JavaScript中的内存管理流程,根其他语言一样也是分为三步:申请---使用---释放
申请:let obj = {};
使用:obj.name = 'lucky cat';
释放:obj = null;
JavaScript中的垃圾
- JavaScript中的内存管理是自动的
- 对象不再被引用时就变成了垃圾
- 对象不能从根上访问到时是垃圾
引用?从根上访问?来看一个概念:可达对象
JavaScript中的可达对象
- 可以访问到的对象就是可达对象(引用、作用域链)
- 可达的标准就是从根出发是否能够被找到,JavaScript中的根可以理解为全局变量对象
举个 🌰:
{name: 'xm'}有两个引用空间
虽然obj被 null 了,{name: 'xm'} 还是可达的,因为 ali 还在引用着它
再举个 🌰:
用图示表示,如下图:
如果,我们在代码对obj1的引用都delete掉,如图中红色线条:
那么此时,从根上我们是不能找到 obj1 这个对象的,所以它就变成了内存中的垃圾。JavaScript 引擎就会找到它,然后对它进行回收。
GC (垃圾回收机制)
定义和作用
GC 是垃圾回收机制的简写
GC 可以找到内存中的垃圾、并释放和回收空间
垃圾是什么
- 程序中不再需要使用的对象
function fun() {
name = 'lucky cat';
return `Hello, ${name}`;
}
fun();
- 程序中不能再访问到的对象
function fun() {
const name = 'lucky cat';
return `Hello, ${name}`;
}
fun();
GC 算法是什么
- GC 是一种机制,垃圾回收器完成具体的工作
- 工作的内容就是查找垃圾释放空间、回收空间
- 算法就是工作时查找和回收所遵循的规则
常见的GC算法
- 引用计数
- 标记清除
- 标记整理
- 分代回收
引用计数
核心思想
设置引用数,判断当前引用数是否为 0
当引用关系改变时,引用计数器就会主动的修改引用数字,引用数字为 0 时,GC 就会立即工作,将当前空间立即回收
优缺点
优点
-
发现垃圾时立即回收
-
最大限度减少程序暂停
时刻监控着,在内存快要占满的时候,就会去找那 些引用计数为 0 的空间进行释放
缺点
-
无法回收循环引用的对象
比如:obj1.name = obj2 obj2.name = obj1
-
时间开销大
要一直维护一个数字的变化,时刻监控这个一个引用对象的数值是否要修改
标记清除
核心思想
分标记和清除两个阶段完成
遍历所有对象找标记活动对象,遍历所有对象清除没有标记对象,回收相应的空间
图示:
从全局可以找到a、b、c三个可达对象,如果发现他们下边还有孩子,则会采用递归的方式进行寻找其他可达对象,然后对可达对象进行标记;而a1和b1从全局的链条上是找不到a1和b1的,所以GC 机制就认为他们是垃圾对象。
优缺点
优点
- 可以回收循环引用的对象
缺点
- 容易产生碎片化空间,浪费空间,不能让空间最大化的使用
假设我们 a 和 c 释放了,a 释放了 3 个字节,c 释放了 1 个字节,看起来是 4 个字节,但是他们是分散的,地址不连续,中间可能还有 b 分隔着,如果后续我们想申请一个 2 个字节的空间,找 a 释放的空间则多了,找 c 释放的空间则不够,所以就造成了空间碎片化。
-
不会立即回收垃圾对象
在遍历过程中即使知道也只是打个标记,最后才一起回收
标记整理
核心思想
标记整理可以看作是标记清除的增强
标记阶段的操作跟标记清除一致,清除阶段会先执行整理,移动对象位置
图示:
优缺点
跟标记清除相比,弥补了空间碎片化的不足
分代回收
分代回收是v8垃圾回收的策略
将内存分为新生代、老生代
针对不同对象采用不同的算法
具体下篇整理。。
此文乃前端小白的学习总结,请多多担待。(^_^)