JavaScript内存管理

内存:由可读写单元组成,表示一片可操作空间
管理:认为的去操作一篇空间的申请、使用和释放
内存管理:开发者主动申请空间、使用空间、释放空间
管理流程:申请-使用-释放

// memory management

// 申请
let obj = {}
// 使用
obj.name = 'Tom'
// 释放
obj = null
复制代码

垃圾回收

JavaScript中内存管理是自动的。每当我们创建一个数组、对象、函数的时候,就会自动的去分配相应的内存空间。后续程序运行过程中当对象不再被引用时就会被看作垃圾,或者对象不能从根本上访问时也会被看作垃圾。此时JavaScript引擎就会出来工作,将这些垃圾占据的内存空间进行回收,这就是垃圾回收机制。
可以访问到的对象就是可达对象(引用、作用域链),可达的标准就是从根出发是否能被找到。JavaScript中的根就可以理解为全局变量对象。

// 定义一个变量obj指向一个对象,这个对象占据了一个名为Tom的内存空间,obj对象引用了Tom空间。
//在全局执行上下问中obj是可以被找到的,这个obj对象是可达的,这也说明Tom空间也是可达的
let obj = {name: 'Tom'}
// 定义一个ali变量等于obj,在这里可以认为Tom空间又多了一层引用,存在引用数值变化
let ali = obj
// 此时Tom空间还是可达的,因为ali还在引用中
obj = null
复制代码

GC算法

GC就是垃圾回收机制的简写。
GC可以找到内存中的垃圾,并释放和回收空间。

GC里的垃圾是什么?

  • 程序中不再需要使用的对象
function func(){
    name = 'lg'
    return `${name} is a coder`
}
func()
复制代码
  • 程序中不能再访问的对象
function func(){
    const name = 'lg'
    return `${name} is a coder`
}
func()
复制代码

GC算法是什么?

GC是一种机制,垃圾回收器完成具体的工作。工作的内容就是查找垃圾、释放空间、回收空间。算法就是工作时查找和回收所遵循的规则。

常见GC算法

引用计数

核心思想:设置引用数,判断当前引用数是否为0来决定是不是一个垃圾对象
当某个对象引用关系改变的时候,引用计数器就会主动修改当前对象所对应的引用数字。当引用数字为0的时候,GC就开始工作,将其所在对象空间进行回收和释放再使用。

const user1 = {age: 11}
const user2 = {age: 22}
const user3 = {age: 33}
const nameList = [user1.age, user2.age, user3.age]
function fn(){
    const num1 = 1
    cosnt num2 = 2
}
fn()
复制代码

当前环境下,num1和num2只在fn的作用域内起作用,当fn()执行结束后,在外部全局中就找不到num1和num2,此时num1和num2的引用计数就变为0,GC开始工作将num1和num2当作垃圾进行回收。而nameList中引用了user1、user2和user3,所以它们没有被当作垃圾被回收。
引用计数算法优点

  • 发现垃圾时立即回收
  • 最大限度减少程序暂停

引用计数算法优点

  • 无法回收循环引用的对象
  • 时间开销大
function fn(){
    const obj1 = {}
    const obj2 = {}
    obj1.name = obj2
    obj2.name = obj1
    return 'lg is a coder'
}
fn()
复制代码

当fn()执行结束后,其内部空间肯定会涉及到空间回收的情况。比如obj1和obj2,在全局中已经不再指向它们了,它们的引用计数应该为0,但是在fn内部obj1和obj2通过name属性相互引用,因此它们的引用计数不为0,GC就没法通过引用计数算法进行回收。

标记清除

核心思想:分标记和清除两个阶段完成
标记清楚算法会将整个垃圾回收阶段分为两个阶段,第一个阶段是遍历所有对象找到活动对象进行标记操作,第二个阶段是遍历所有对象清除没有标记的对象,同时也会把第一个阶段设置的标记给抹掉,便于GC下次还能正常工作。通过两次的遍历行为把当前的垃圾空间进行回收,最终再交给空闲列表进行维护,方便后续程序执行。
标记清除算法优点
相对于引用计数算法,解决了对象循环引用不能回收问题
标记清除算法缺点

  • 会造成空间碎片化,不能使空间得到最大化的使用
  • 不能立即回收垃圾对象

标记整理

标记整理可以看作是标记清除的增强。标记阶段的操作和标记清除一致;清除阶段会先执行整理,移动对象的位置。
标记整理算法优点
减少碎片化空间
标记整理算法缺点
不能立即回收垃圾对象

V8

V8是一款主流的JavaScript执行引擎。

  • V8采用即时编辑,执行速度快。
  • V8内存设置上限。

V8采用分代回收的思想,将内存空间分成新生代存储区和老生代存储区,根据不同代采用最高效的算法,从而对不同对象进行回收的操作。

新生代对象回收实现

V8内存空间一分为二,小空间用于存储新生代对象。新生代指的是存活时间较短的对象。
回收过程采用复制算法+标记整理。首先会将新生代内存区分为两个等大的空间,使用空间为From,空闲空间为To。所有的活动对象存储于From空间,进行标记整理后将活动对象拷贝至To空间,From与To交换空间完成释放。
拷贝过程中可能会出现晋升。晋升就是将新生代对象移动至老生代进行存储。至于说什么时候会触发晋升操作呢?一般有两个判断标准,一轮GC操作后还存活的新生代需要晋升;To空间使用率超过25%,此时也需要把活动对象移动到老年存储区中进行存放。

老生代对象回收实现

主要采用标记清除、标记整理、增量标记算法。首先使用标记清除完成垃圾空间的回收。当把新生代区域内容往老生代区域移动的时候,老生代区域空间不足以存放新生代移动来的对象,此时会触发标记整理操作,进行空间的优化。最后用增量标记(增量标记是程序和标记交替进行)进行效率优化。
新生代区域垃圾回收使用空间换时间,老生代区域垃圾回收不适合复制算法。

界定内存问题的标准

  • 内存泄漏:内存使用持续升高
  • 内存膨胀:在多数设备上都存在性能问题
  • 频繁垃圾回收:通过内存变化图进行分析

监控内存的几种方式

  • 浏览器任务管理器
  • Timeline时序图记录
  • 堆快照查找分离DOM
  • 判断是否存在频繁的垃圾回收
    确定频繁的垃圾回收方法

    • Timeline中频繁的上升下降
    • 任务管理器中数据频繁的增加减少
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享