当前位置: 首页 > news >正文

JS垃圾回收与内存泄漏

垃圾回收与内存泄漏

内存泄露

程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。
对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。
也就是说,不再用到的内存,如果没有及时释放,就叫做内存泄漏(memory leak)。

JavaScript 中的垃圾回收

浏览器的 Javascript 具有自动垃圾回收机制(GCGarbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存

但是这个过程不是实时的,因为其开销比较大并且 GC 时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。

不再使用的变量也就是生命周期结束的变量,当然只可能是局部变量,全局变量的生命周期直至浏览器卸载页面才会结束。局部变量只在函数的执行过程中存在,而在这个过程中会为局部变量在栈或堆上分配相应的空间,以存储它们的值,然后在函数中使用这些变量,直至函数结束,而闭包中由于内部函数的原因,外部函数并不能算是结束。

下面是一段示例代码:

function fn1() {
    var obj = {name: 'xiaocan', age: 23};
}
function fn2() {
    var obj = {name:'xiaocantongxue', age: 23};
    return obj;
}

var a = fn1();
var b = fn2();

在上面的代码中,我们首先声明了两个函数,分别叫做 fn1fn2

fn1 被调用时,进入 fn1 的环境,会开辟一块内存存放对象 {name: ‘xiaocan’, age: 10}。而当调用结束后,出了 fn1 的环境,那么该块内存会被 JavaScript 引擎中的垃圾回收器自动释放;

fn2 被调用的过程中,返回的对象被全局变量 b 所指向,所以该块内存并不会被释放。

这里问题就出现了:到底哪个变量是没有用的?

所以垃圾收集器必须跟踪到底哪个变量没用,对于不再有用的变量打上标记,以备将来收回其内存。

用于标记的无用变量的策略可能因实现而有所区别,通常情况下有两种实现方式:标记清除引用计数

引用计数不太常用,标记清除较为常用。

标记清除

JavaScript 中最常用的垃圾回收方式就是标记清除。

当变量进入环境时,例如,在函数中声明一个变量,就将这个变量标记为“进入环境”。

从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。

而当变量离开环境时,则将其标记为“离开环境”。

function test(){
  var a = 10 ; // 被标记 ,进入环境 
  var b = 20 ; // 被标记 ,进入环境
}
test(); // 执行完毕 之后 a、b 又被标离开环境,被回收。

垃圾回收器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。

然后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记(闭包)。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。

最后,垃圾回收器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。

到目前为止,IE9+、Firefox、Opera、Chrome、SafariJS 实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。

引用计数

引用计数的含义是跟踪记录每个值被引用的次数。

当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1

相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。

这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为 0 的值所占用的内存。

function test() {
    var a = {};	// a 指向对象的引用次数为 1
    var b = a;	// a 指向对象的引用次数加 1,为 2
    var c = a;	// a 指向对象的引用次数再加 1,为 3
    var b = {};	// a 指向对象的引用次数减 1,为 2
}

Netscape Navigator3 是最早使用引用计数策略的浏览器,但很快它就遇到一个严重的问题:循环引用

循环引用指的是对象 A 中包含一个指向对象B的指针,而对象 B 中也包含一个指向对象 A 的引用。

function fn() {
    var a = {};
    var b = {};
    a.pro = b;
    b.pro = a;
}
fn();

以上代码 ab 的引用次数都是 2fn 执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为 ab 的引用次数不为 0,所以不会被垃圾回收器回收内存,如果 fn 函数被大量调用,就会造成内存泄露。在 IE7IE8 上,内存直线上升。

JavaScript中的垃圾回收站机制

JavaScript 具有自动垃圾回收机制。垃圾收集器会按照固定的时间间隔周期性的执行。

JavaScript 常见的垃圾回收方式:标记清除引用计数方式。

1、标记清除方式:

  • 工作原理:当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。

  • 工作流程:

  • 垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记;

  • 去掉环境中的变量以及被环境中的变量引用的变量的标记;

  • 被加上标记的会被视为准备删除的变量;

  • 垃圾回收器完成内存清理工作,销毁那些带标记的值并回收他们所占用的内存空间。

2、引用计数方式:

  • 工作原理:跟踪记录每个值被引用的次数。

  • 工作流程:

  • 声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是 1

  • 同一个值又被赋值给另一个变量,这个引用类型值的引用次数加 1

  • 当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减 1

  • 当引用次数变成 0 时,说明没办法访问这个值了;

  • 当垃圾收集器下一次运行时,它就会释放引用次数是 0 的值所占的内存。


以上笔记整理于渡一教育谢老师课堂

相关文章:

  • MySQL数据库笔记
  • 基于SSM的高校餐厅防疫管理系统
  • y118.第七章 服务网格与治理-Istio从入门到精通 -- Istio流量治理快速入门(四)
  • Linux | 性能问题排查
  • 【微服务容器化】第二章-Docker常用命令
  • 慧差\畸变 zemax示例
  • 硬件控制方法
  • 【单细胞高级绘图】08.细胞通讯_两组比较_气泡图
  • java计算机毕业设计基于安卓Android/微信小程序的校园报修管理APP
  • Mysql 的安装
  • 8.【外部排序】基本概念和方法 + 优化:【败者树】{减少关键字对比次数}、【置换-选择 排序】{减少初始归并段数量}、【最佳归并树】{谁先合并更快}
  • Python装饰器通俗理解
  • 1516. 移动 N 叉树的子树 DFS
  • 【计算机图形学】高级外观建模
  • 阿里云dataworks中业务流程中问题(odps2)
  • SegmentFault for Android 3.0 发布
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • 8年软件测试工程师感悟——写给还在迷茫中的朋友
  • CSS 专业技巧
  • Golang-长连接-状态推送
  • Java编程基础24——递归练习
  • Java到底能干嘛?
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 好的网址,关于.net 4.0 ,vs 2010
  • 缓存与缓冲
  • 回流、重绘及其优化
  • 排序算法之--选择排序
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 我建了一个叫Hello World的项目
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 一道面试题引发的“血案”
  • 异常机制详解
  • 转载:[译] 内容加速黑科技趣谈
  • 阿里云IoT边缘计算助力企业零改造实现远程运维 ...
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • 树莓派用上kodexplorer也能玩成私有网盘
  • (3)nginx 配置(nginx.conf)
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (C语言)字符分类函数
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (动态规划)5. 最长回文子串 java解决
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (利用IDEA+Maven)定制属于自己的jar包
  • (万字长文)Spring的核心知识尽揽其中
  • (一)基于IDEA的JAVA基础1
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)清华学霸演讲稿:永远不要说你已经尽力了
  • .net core 6 redis操作类
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .net 中viewstate的原理和使用
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • .Net的DataSet直接与SQL2005交互