内存为什么需要管理?
如果我们在写代码时,不了解内存机制,就会让我们编写出一些不易察觉的内存性问题代码。这种问题多了以后会给程序带来一些意想不到的 bug,所以掌握内存管理十分必要。
内存管理介绍
- 内存:由可读写单元组成,表示一片可操作空间
- 管理:由人主动去操作这片空间的申请,使用和释放
- 内存管理:开发者主动申请空间,使用空间,释放空间
- 管理流程:申请 — 使用 — 释放
JS 中的内存管理
ES 当中,没有提供相应的操作 API,所以 JS 不能像是 C 或者 C++ 那样由开发者主动调用相应的 API 来完成空间管理,但是这并不影响我们通过 JS 脚本来演示当前在内部一个空间的生命周期是怎样完成的。
- 申请使用空间
- 使用内存空间
- 释放内存空间
// 申请空间
let obj = {}
// 使用空间
obj.name = 'xl'
// 释放空间
obj = null
JS 中的垃圾回收
JS 中什么内容会被当作垃圾回收?
- JS 中内存管理是自动的。
每当创建一个对象,数组,或者函数时,就会自动分配一块内存空间。 - 对象不再被引用时是垃圾。
后续程序在执行的过程中,如果通过一些引用关系,无法找到某些对象的时候,这些对象会被当作垃圾。 - 对象不能从根上访问到时是垃圾。
这些对象其实是存在的,但是由于代码当中一些不合适的语法或者结构性的错误,让我们没有办法再去找到这个对象,那这个对象也会被称为垃圾。
知道了什么是垃圾之后,JS 执行引擎就会出来工作,把他们占的对象空间进行回收,这个过程就是所谓的 JS 垃圾回收。
JS 中的可达对象
可以访问到的对象就是可达对象(引用 / 作用域链)。
可达的标准就是从根出发是否能够被找到。
JS 中的根可以理解为全局变量对象。
JS 中的垃圾回收,其实就是找到垃圾然后让 JS 执行引擎来进行空间的释放和回收,在这里我们用到了引用和可达对象。
JS 中的引用和与可达
引用:
let obj = { name: 'xl'}
/**
* 这行代码相当于这个空间被obj引用了
* 当前这个obj是可以从根上找到的,所以这个obj是可达的
* */
let ali = obj
/**
* xl的空间又多了一次引用
* 所以存在了一次引用数值变化,这个概念之后引用计数算法当中会被用到
* */
obj = null
/**
* 这行代码之后
* 原本xl这个对象空间是由两个引用的,而随着这行的执行
* obj到xl空间的引用,相当于是断掉了
* 但现在当前这个xl对象还是可达的,因为ali还在引用着
* 这就是引用的主要说明,顺便看到了一个可达
* */
可达:
function objGroup(obj1, obj2) {
obj1.next = obj2
obj2.prev = obj1
return {
o1: obj1,
o2: obj2
}
}
/**
* 首先,我们定义了一个函数并接受了两个变量,一个是obj1一个是obj2
* 然后在内部让其有一个属性互相指引
* 通过return返回这样一个结果,然后在外部调用函数
* 现在的对象不管是o1还是o2再或者是objGroup返回的这个对象
* 都可以从根上找到
* */
let obj = objGroup({name: 'obj1'}, {name: 'obj2'})
console.log(obj)
/**
* {
* o1: { name: 'obj1', next: { name: 'obj2', prev: [Circular] } },
* o2: { name: 'obj2', prev: { name: 'obj1', next: [Circular] } }
* }
* */
到底什么是垃圾?
我们现在对可达里边的代码进行一些操作:
将 obj 对象内部对 obj1 的引用删除,然后将 obj2.prev 删除。
这个时候我们没有办法通过任何方式获取到 obj1。这时 o1 会被认为是一个垃圾,最后 JS 引擎会找到他并进行一个回收。
我们在编写代码时,会存在一些对象引用的关系。我们可以从根的下边来进行查找,按照链条,我们终归能找到某一个对象。如果说找到对象的一些路径被破坏掉,或者被回收了,这个时候我们没办法再找到他。最后我们会把他当作垃圾,可以让垃圾回收机制将其回收掉。