前端实战篇-聊聊JavaScript内存

西域战神 2018-03-01 09:18:05 ⋅ 587 阅读


C、C++语言需要手动管理内存的分配与释放(常用方法:malloc(), calloc(), realloc()和free()等)。而JavaScript与Java、C#相似,内置了垃圾回收器,能自动管理内存的分配与释放。

内存生命周期
分配内存
使用分配的内存(读与写操作)
当应用程序不再需要时,释放掉已分配的内存


虽然垃圾回收器能能自动管理内存分配、释放,但并不意味着开发者不再需要关注内存管理。因为一些不好的编码会导致内存泄露,即应用程序不再需要的内存没有被释放掉。因此了解内存管理是很重要的。

Javascript中的内存分配

当声明变量时,JavaScript会自动为变量分配内存
var numberVar = 100; //为整数分配内存var
stringVar = 'node simplified';  // 为字符串分配内存
var objectVar = {a: 1};
// 为对象分配内存
var a = [1, null, 'abra'];
// 为数组分配内存
function f(a) {  
   return a + 2; }
// 为函数分配内存

GC(Garbage collection)

引用计数算法是一种最基础的垃圾回收算法,当一个对象的引用数为零时,会被自动回收。该算法将一个对象的引用数为0时视为应用程序不再需要的内存。

!function (){var o1 = {a: {b: 2}},// 两个对象被创建。假如分别用A:{a: {b: 2}},B:{b: 2}表示,对象B被对象A的属性a引用,对象A被赋值给变量o1。A和B的引用数都为1,因此不能被回收。
      o2 = o1; // 将对象A赋给变量o2。此时A引用数为2,B引用数1。
      o1 = 1;// 将变量o1对对象A引用切断。此时A引用数为1,B引用数1。var oa = o2.a; // 将对象B赋值给变量oa。此时A引用数为1,B引用数2。
      o2 = 'foo'; // 将变量o2对对象A引用切断。此时A引用数为0,B引用数1。因为对象A的a属性被变量oa引用,因此对象A不能被释放。
      oa = null; // 将变量oa对对象B引用切断。此时A引用数为0,B引用数0。A与B会被回收。}()

引用计数的限制:循环引用

循环引用存在一个限制。如下实例,两个对象相互引用,形成一个循环引用。正常情况下,当函数执行完后,对应的内存会被释放掉。而引用计数算法会将循环引用对象的引用数都视为至少为1,因此不能被回收。

function f() {  var o = {};  var o2 = {};
  o.a = o2; // o references o2
  o2.a = o; // o2 references o
   return 'azerty';
}f();


常见问题

IE6-7的DOM对象是基于计数引用算法进行垃圾回收的。而循环引用通常会导致内存泄露:

var div;window.onload = function() {
  div = document.getElementById('myDivElement');                                  div.circularReference = div;
  div.lotsOfData = new Array(10000).join('*');
};

DOM元素div通过自身的“circularReference”属性循环引用自己。如果没有显式将该属性删除或设为null,计数引用垃圾回收器会始终持有至少一个引用。即使DOM元素从DOM树种移除,DOM元素的内存会一直存在。如果DOM元素持有一些数据(如实例中“lotsData”属性),该数据对应的内存也无法被释放。

Mark-and-sweep algorithm(标记清除)

该算法将“对象不再需要”的定义简化为“对象不可到达”。 这个算法假设有一组被称为roots的对象(在JavaScript中,root就是全局对象)。垃圾回收器会定期地从这些roots开始,查找所有从根开始引用的对象,然后再查找这些对象引用的对象……。从roots开始,垃圾回收器会查找所有可到达对象,并回收不可到达的对象。

为了确定对象是否需要,该算法要确定对象是否可到达。由如下步骤组成:

垃圾回收器会创建一组roots,roots通常是持有引用的全局变量。在JavaScript中,window对象就可作为root的全局变量。

垃圾回收器会检查所有的roots并标记为活跃状态。然后递归遍历所有的子变量。只要从root不能到达的都被标记为垃圾。

所有没有被标记为活跃状态的内存块都被视为垃圾。垃圾回收器就可以释放这部分内存并把释放的内存返回给操作系统。


这个算法比引用计数算法更优,因为对于引用计数算法“零引用的对象”总是不可到达的,但反之则不一定,如循环引用。而标记清除算法不存在循环引用的问题。

截至2012年,所有现代浏览器都内置了标记清除垃圾回收器。在过去几年里所有对JavaScript垃圾回收算法的改进都是基于标记清除算法来实现的,但并没有改变标记清除算法本身和它对“对象不再需要”定义的简化。

作者:zhansingsong
地址:https://github.com/zhansingsong/js-leakage-patterns

更多精彩内容请关注“IT实战联盟”公众号哦!!!




全部评论: 0

    我有话说:

    前端实战—在Javascript中,Number类型超长问题详解

    今天给大家分享的是在Javascript中,获取到数字超出长度问题。

    前端实战JavaScript 反调试技巧的简单应用(上)

    最近作者看了一些关于JavaScript反调试的帖子,今天给大家整理一下希望有帮助。

    移动H5前端五大性能优化方案(实战

    移动H5前端五大性能优化方案(实战

    前端实战JavaScript 反调试技巧的简单应用(下)

    通过时间差异、DevTools检测(Chrome)、隐式流完整性控制和 代理对象来达到JavaScript反调试目的

    JAVA实现附近范围公交定位问题

    接上前端实战:通过JS抓取城市所有站点与线路】获取附近定位信息

    前端实战:通过JS抓取城市所有站点与线路

    做公交线路定位,木有数据怎么办?网上抓去~ 手把手教你通过JS实现站点线路数据抓取

    抖音品质建设 - iOS启动优化《实战

    实战,本文是实战。 原理:抖音品质建设-iO...

    推荐一款前端数据源管理工具 algeb

    ALGEB 简介 这是一个比较抽象的库,一开始可能比较难理解。我写它的初衷,是创建可响应的数据请求管理。在传统数据请求中,我们只是把携带ajax代码的一堆函数放在一起,这样就可以调用接口。但是这种方案不是很灵活,无法解决共享数据源,数据没回来时怎...

    SpringBoot+zk+dubbo架构实践(四):sb+zk+dubbo框架搭建(附源码GitHub地址)

    案例模拟了一个provider服务提供方和PC、Web两个服务消费方附GitHub源码......

    MySql实战:写一个简单的存储过程,完成订单定时任务

    前言之前我们分享了MySql的性能优化、索引详解等内容,本文章主要是针对想要入门MySql存储过程的读者,主要实现的业务是订单库里面的超过30分钟没有支付的订单全部置为失效订单......

    Node实战:Express-session解析(八)

    Session和HTTP协议属于不同层面的事物,HTTP属于ISO七层模型的最高层应用层,前者Session不属于后者,前者HTTP是具体的动态页面技术来实现的,但同时它又是基于后者的。

    MongoDB实战:数据库备份与恢复/导出与导入

    mongodump命令可以通过参数指定导出的数据量级转的服务器......

    ECharts 5.0.1 发布,JavaScript 实现的交互式图表可视化库

    Apache ECharts (incubating) 5.0.1 已发布,ECharts 是一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容

    微信小程序实战:商品属性联动选择(案例)

    本期的微信小程序实战来做一个电商网站经常用到的-商品属性联动选择的效果,素材参考了一点点

    iOS实战:[译]iOS扩充--OCR光学字符识别(附项目GitHub地址)

    OCR(Optical Character Recognition) 光学字符识别, 是从图像中电子扫描提取文本的过程, 可以在文档编辑等多种形式重用它,例如: 文本搜索/压缩等用途。

    Node.js 实战--微信支付系列(二)

    接上一首先我们看一下整体上微信小程序的开发流程图

    ngrok网穿透服务部署记录

    ngrok,一个用于实现网穿透服务,golang写的,已经很久远的一个东西了,可自己部署的版本最后一个版本是1.7.1,很久也没更新了,但他还是比较稳妥的

    架构实战:MyBatis一级、二级,并整合ehcache分布式缓存的使用,附演示实例

    ehcache是一个纯Java的进程缓存框架,是一种广泛使用的开源Java分布式缓存,具有快速、精干等特点,是Hibernate中默认的CacheProvider。