内存回收

我心飞翔 分类:javascript

内存分配

JS的类型有8种:Number、String、Boolean、Undefined、Null、Symbol、bigInt、Object,其中Number、String、Boolean、Undefined、Null、Symbol、bigInt为基础类型,Object(JSON、Array、Date等)为复杂类型.JS引擎在解析代码时,如果遇到基础类型的值,那么将会在栈内存中开辟一个空间(大小固定)用来存储这个值;如果遇到复杂类型的值,那么将会在栈内存和堆内存分别开辟一个空间,其中堆内存(大小不固定)用来存储复杂类型的数据,栈内存(大小固定)用来存储这个堆内存空间的地址.使用数据时,如果数据类型为基础类型,那么直接从栈内存读取;如果数据为复杂类型,那么首先从栈内存中这个数据在堆内存中存储空间的地址,然后通过这个地址去堆内存中查找数据(按引用访问).
栈内存空间由OS操作分配,存储的数据按照先进后出,使用一级缓存,当数据调用完毕,由OS自动操作释放空间
堆内存空间由程序员分配,使用二级缓存,若程序员不主动释放,那么需要等待程序结束时由OS根据垃圾回收算法判断生命周期后进行回收.

// 将b的栈内存内容覆盖a的栈内存内容
a = b;
 

ES6中的weakMapweakSet里的引用都是弱引用,不会计入垃圾回收机制,只要引用清除,内部的引用就会被垃圾回收清除.

垃圾回收

通常情况下,垃圾数据回收分为手动回收和自动回收两种策略.而JS属于自动回收策略,由于垃圾回收机制开销比较大,所以垃圾收集器会按照固定的时间间隔周期性执行回收.
局部变量存在于其函数执行上下文中的变量对象中,当函数执行完毕,函数执行上下文出栈,垃圾收集器判断变量对象中的变量并进行回收.全局变量存在于全局执行上下文的变量对象中,它存在于整个window的生命周期中,同时垃圾收集器对全局变量的判断是很难的.

垃圾回收算法通过判断变量所占的内存是否被占用来进行回收,而常用的算法有:引用计数和标记清除.

引用计数

判断一个对象是否有指向它的引用,如果没有则回收.但是这会带来一个问题:循环引用.如果两个对象互相引用,那么垃圾回收器就不会进行回收,最终可能导致内存泄漏.这也是现代浏览器不再使用这个算法的原因.

标记清除

从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象进行保留,那些从根部出发无法触及的对象被标记为不再使用,稍后进行回收.

  1. 当变量进入环境时,标记为“进入环境”,离开环境时标记为"离开环境"
  2. 垃圾回收器在运行时会给存储在内存中的所有变量都加上标记(全局对象,变量对象,活动对象)
  3. 然后它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包),即移除执行栈中的函数执行上下文中变量对象的标记
  4. 而在此之后再被加上标记的变量将被视为准备删除的变量(周期扫描)
  5. 垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间

GC缺陷

当执行垃圾回收时,停止响应其他操作(单线程).这时候可以通过“分代回收”,区分临时和持久对象(多回收临时对象区,少回收持久对象区,减少每次遍历的对象,从而减少每次GC耗时)以避免GC造成的长时间停止响应.

内存泄漏

对于持续运行的服务进程,必须及时释放不再用到的内存.否则内存占用越来越高,轻则影响系统性能,重则导致进程崩溃.对于不再用到的内存,没有及时释放,就叫做内存泄漏.

  • 意外的全局变量,垃圾回收器不会清除全局变量
  • 闭包,闭包引用的变量会一直保存在内存中
  • 未销毁的定时器和回调函数
  • 不规范的DOM引用

回复

我来回复
  • 暂无回复内容