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

内存生命周期:

  1. 分配内存
  2. 使用分配的内存(读与写操作)
  3. 当应用程序不再需要时,释放掉已分配的内存

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

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)

垃圾回收是追踪并释放应用程序不再使用的内存过程。垃圾回收器通过算法来实现追踪应用程序不再使用的内存。主要涉及的垃圾回收算法如下:

  • Reference-counting garbage collection(引用计数)
  • Mark-and-sweep algorithm(标记清除)

Reference-counting 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”属性),该数据对应的内存也无法被释放。了解更多参考--->IE<8循环引用导致的内存泄露

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

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

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

  1. 垃圾回收器会创建一组roots,roots通常是持有引用的全局变量。在JavaScript中,window对象就可作为root的全局变量。
  2. 垃圾回收器会检查所有的roots并标记为活跃状态。然后递归遍历所有的子变量。只要从root不能到达的都被标记为垃圾。
  3. 所有没有被标记为活跃状态的内存块都被视为垃圾。垃圾回收器就可以释放这部分内存并把释放的内存返回给操作系统。

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

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

循环引用不再是问题

前面循环引用的实例中,在函数执行完后,两个对象不再被全局对象可访问的对象引用。因此这两个对象被垃圾回收器标记为不可到达,接着被回收掉。 

限制:需要明确无法到达的对象

对于这个限制,实践中很少遇见,所以开发者不太会去关心垃圾回收机制。

参考文章:

原文地址: https://github.com/cucygh/js-leakage-patterns/blob/master/JavaScript%E5%86%85%E5%AD%98%E9%82%A3%E7%82%B9%E4%BA%8B/JavaScript%E5%86%85%E5%AD%98%E9%82%A3%E7%82%B9%E4%BA%8B.md

JavaScript中的内存释放的更多相关文章

  1. JavaScript 中对内存的一些了解

    在使用JavaScript进行开发的过程中,了解JavaScript内存机制有助于开发人员能够清晰的认识到自己写的代码在执行的过程中发生过什么,也能够提高项目的代码质量.其实关于内存的文章也有很多,写 ...

  2. JavaScript中的内存泄漏以及如何处理

    随着现在的编程语言功能越来越成熟.复杂,内存管理也容易被大家忽略.本文将会讨论JavaScript中的内存泄漏以及如何处理,方便大家在使用JavaScript编码时,更好的应对内存泄漏带来的问题. 概 ...

  3. javascript中的内存管理和垃圾回收

    前面的话 不管什么程序语言,内存生命周期基本是一致的:首先,分配需要的内存:然后,使用分配到的内存:最后,释放其内存.而对于第三个步骤,何时释放内存及释放哪些变量的内存,则需要使用垃圾回收机制.本文将 ...

  4. Javascript中的内存泄漏

    最新博客站点:欢迎来访 一.内存泄漏        由于某些原因不再需要的内存没有被操作系统或则空闲内存池回收.编程语言中有多种管理内存的方式.这些方式从不同程度上会减少内存泄漏的几率,高级语言嵌入了 ...

  5. javascript中的内存管理

    目录 简介 内存生命周期 JS中的垃圾回收器 引用计数垃圾回收算法 Mark-and-sweep回收算法 调试内存问题 闭包Closures中的内存泄露 javascript中的内存管理 简介 在c语 ...

  6. JavaScript 中的内存泄漏

    JavaScript 中的内存泄漏 JavaScript 是一种垃圾收集式语言,这就是说,内存是根据对象的创建分配给该对象的,并会在没有对该对象的引用时由浏览器收回.JavaScript 的垃圾收集机 ...

  7. 浅谈JavaScript中的内存管理

    一门语言的内存存储方式是我们学习他必须要了解的,接下来让我浅谈一下自己对他的认识. 首先说,JavaScript中的变量包含两种两种类型: 1)值类型或基本类型:undefined.null.numb ...

  8. JavaScript 中的内存和性能、模拟事件(读书笔记思维导图)

    由于事件处理程序可以为现代 Web 应用程序提供交互能力,因此许多开发人员会不分青红皂白地向页面中添加大量的处理程序.在 JavaScript 中,添加到页面上的事件处理程序数量将直接关系到页面的整体 ...

  9. JavaScript中的内存溢出与内存泄漏

    内存溢出 是一种程序运行出现的错误: 当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误 var obj = {} for (var i = 0; i < 100000; i++) ...

随机推荐

  1. 二评北科团委:“斩首计划”进行时,其用心也毒

    近日,在同学们和热心群众的共同努力下,各高校相继悬崖勒马,北大马会.北语新新青年.人大新光等进步社团均顺利注册.而北科团委倒行逆施,可谓一枝独秀,对于齐民学社的同学们的诉求,不仅不予以回应,反而采取表 ...

  2. DB2数据库多行一列转换成 一行一列

    在db2中遇到多行一列转为一行一列的需求时,用db2函数 LISTAGG可以实现该功能.语法如下: SELECT   [分组的字段 ] , LISTAGG([需要聚合的字段名], ',')   FRO ...

  3. Ubuntu12.10下安装Python3

    设置Python3.2为默认版本 ubuntu12.10下默认安装了Python2.7和Python3.2.3,并且设置了Python2.7为默认的版本,因此如果讨厌麻烦的话,可以直接将Python3 ...

  4. html和jsp页面中把文本框禁用,只能读不能写的方法

    方法常用有三种: 第一种,使用   onfocus="this.blur()" <input name="deptno" type="text& ...

  5. VRRP笔记三:配置keepalived为实现haproxy高可用的双主模型配置文件示例:

    说明:其基本实现思想为创建两个虚拟路由器,并以两个节点互为主从. ! Configuration File for keepalived global_defs { notification_emai ...

  6. Typescript - 类型断言

    原文:TypeScript基本知识点整理 零.序言 类型断言,可以用来手动指定一个值的类型. 给我的感觉,和 java 中的强制类型转换很像. 常常和联合类型配合使用,如: // 错误示例 funct ...

  7. 吴裕雄--python学习笔记:sqlite3 模块的使用与学生信息管理系统

    import sqlite3 cx = sqlite3.connect('E:\\student3.db') cx.execute( '''CREATE TABLE StudentTable( ID ...

  8. 吴裕雄--天生自然 R语言开发学习:时间序列(续一)

    #-----------------------------------------# # R in Action (2nd ed): Chapter 15 # # Time series # # r ...

  9. Java JDBC调用inout类型参数的存储过程

    存储过程参数类型:in.out.inout,in:输入类型,out:输出类型,inout:既可输入,也可以输出. 一.JDBC调用inout类型参数的存储过程,并且获得返回值 Class.forNam ...

  10. MySQL rand(随机数)、floor(保留整数)、char(ASCII 转字符)、concat(字符串连接)

    一.MySQL的rand()函数 select rand(); rand()函数,随机0-1之间的数. 二.获得0-10之间的整数(包含0,不包含10) ; 其中floor()去掉小数. 三.获得指定 ...