12
2019
10

js分配内存、使用内存、释放内存、 引用计数

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

1、分配内存

  为了不让程序员费心分配内存,JavaScript 在定义变量时就完成了内存分配

var n = 123; // 给数值变量分配内存

var s = "azerty"; // 给字符串分配内存

var o = {a: 1,b: null}; // 给对象及其包含的值分配内存

  有些函数调用结果是分配对象内存

var d = new Date(); // 分配一个 Date 对象

var e = document.createElement('div'); // 分配一个 DOM 元素

  有些方法分配新变量或者新对象

var a = ["ouais ouais", "nan nan"];

var a2 = ["generation", "nan nan"];

var a3 = a.concat(a2); 

// 新数组有四个元素,是 a 连接 a2 的结果

【存储方式】

  因为原始值占据空间固定,是简单的数据段,为了便于提升变量查询速度,将其存储在栈(stack)中

  由于复杂值的大小会改变,所以不能将其存放在栈中,否则会降低变量查询速度,因此其存储在堆(heap)中,存储在变量处的值是一个指针,指向存储对象的内存处

使用内存

  使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数

var a = 1;

console.log(a);// 读取内存中的值

a = 2; // 写入内存

释放内存

  大多数内存管理的问题都在这个阶段。在这里最艰难的任务是找到“所分配的内存确实已经不再需要了”

  Javascript内嵌了垃圾收集器,用来跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。垃圾收集器会按照固定的时间间隔,或代码执行中预定的收集时间,周期性地执行这一操作

  局部变量只在函数执行的过程中存在。而在这个过程中,会为局部变量在栈(或堆)内存上分配相应的空间,以便存储它们的值。然后在函数中使用这些变量,直到函数执行结束。此时,局部变量就没有存在的必要了。因此可以释放它们的内存以供将来使用。在这种情况下,很容易判断变量是否还有存在的必要;但并非所有情况下都这么容易就能得出结论

  垃圾收集器必须跟踪哪个变量有用哪个变量无用,对于不再有用的变量打上标记,以备将来收回其所占用的内存。用于标识无用变量的策略通常有标记清除和引用计数两种

引用计数

  引用计数是最简单的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收

  下面代码中,两个对象a、b被创建,一个作为另一个的属性被引用,另一个被分配给变量o

var o ={ a: {b:2}}

  o2引用了o

var o2 = o;

  “这个对象”的原始引用o被o2替换了

o = 1;

  现在,“这个对象”有两个引用了,一个是o2,一个是oa

var oa = o2.a;

  最初的对象现在已经是零引用了,然而它的属性a的对象还在被oa引用,所以还不能回收

o2 = "yo";

  a属性的那个对象现在也是零引用了,它可以被垃圾回收了

oa = null;

【循环引用】

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

  引用计数算法有个限制:无法处理循环引用。在下面的例子中,两个对象被创建,并互相引用,形成了一个循环。它们被调用之后不会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收

function f(){

  var o = {};

  var o2 = {};

  o.a = o2; // o 引用 o2

  o2.a = o; // o2 引用 o

  return "azerty";

}

f();

【IE低版本】

  IE8-浏览器中,有一部分对象并不是原生javascript对象,例如,其BOM和DOM中的对象就是使用c++以COM(component Object Model 组件对象模型)对象的形式实现,而COM对象的垃圾回收机制采用的就是引用计数策略。该方式常常造成对象被循环引用时内存发生泄漏

function f(){

    var element = document.getElementById('some_element');

    var myObject = new Object();

    myObject.element = element;

    element.someObject = myObject;

}

fn()

  这个例子在一个DOM元素(element)与一个原生javascript对象(myObject)之间创建了循环引用。其中,变量myObject有一个名为element的属性指向element对象,而变量element也有一个属性名为someObject的属性指向myObject。由于存在这个循环引用,即使将例子中的DOM从页面中移除,它也永远不会被回收

  为了避免类似这样的循环引用,最好是在不使用它们的时候手工断开原生javascript和DOM元素之间的连接

myObject.element = null;

element.someObject = null; 

  将变量设置为null意味着切断变量与它此前引用的值之间的连接。当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存

  为了解决此问题,IE9把BOM和DOM对象都转换成了真正的javascript对象

原文链接:https://www.qiquanji.com/post/7000.html

本站声明:网站内容来源于网络,如有侵权,请联系我们,我们将及时处理。

gzh

微信扫码关注

更新实时通知

« 上一篇 下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。