前言:
首先读之前我们要了解几个基本概念
基本类型:Undefined/Null/Boolean/Number/String 放在栈内存中
引用类型:object 放在堆内存中
js不允许直接访问内存中的位置,也就是不能直接访问操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。
堆内存:程序员向操作系统申请一块内存,当系统收到程序的申请时,会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。分配的速度较慢,地址不连续,容易碎片化。此外,由程序员申请,同时也必须由程序员负责销毁,否则导致内存泄露。(内存泄露:下文会解释)
1.什么是动态分配内存?
动态内存分配就是内存不是在程序启动时分配,而是在运行中分配
2.为什么要动态分配内存?
1.有些程序启动时并不清楚需要分配多少内存,得等到运行时才能清楚要分配多少。
2.为了更高效的利用内存。某些内存只在运行中的某个时段需要使用,运行中的其他时段可以另作他用。
3.什么是内存泄露?
不再用到的内存,没有及时释放,就叫做内存泄漏。(越攒越多最后就炸了)
4. js内存泄露存在吗?
误区一:提供内存垃圾回收机制的编程语言如Java、VB、.NET,能完全避免内存泄漏。
这类编程语言需要将引用设为null,然后内存才会自动释放。
所以,虽然JS支持垃圾回收机制,但是JS也会发生内存泄露
5.什么是垃圾?
通俗来说,没有被引用的对象就是垃圾(还有一种情况:几个对象相互引用形成了一个环,相互引用,但是根访问不到他们)(本地函数的局部变量和参数,当前嵌套调用链上的其他函数的变量和参数,全局变量等。这些无法删除统称为根)
6.什么是垃圾回收机制?(GC)
有些语言(比如 C 语言)必须手动释放内存,程序员负责内存管理。这很麻烦,所以大多数语言提供自动内存管理,减轻程序员的负担,这被称为"垃圾回收机制"(garbage collector)。
7.垃圾回收机制的必要性?
下面这段话引自《JavaScript权威指南(第四版)》
由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。
8.js垃圾回收机制的常见两种方法:
引用计数法:(IE等老牌浏览器)
语言引擎有一张"引用表",保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。
左下角的两个值,没有任何引用,所以可以释放上面代码中,数组[1, 2, 3, 4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它还是会持续占用内存。
如果增加一行代码,解除arr对[1, 2, 3, 4]引用,这块内存就可以被垃圾回收机制释放了。
标记-清除算法(谷歌等现代浏览器)
1.垃圾回收机制从根开始一步步向下标记它们的引用
2. 删掉那些没被标记的对象
9.常见的js内存泄露方式
1.意外的全局变量
未定义的变量会在全局对象创建一个新变量。(在浏览器中,全局对象是 window)
另一种意外的全局变量可能由this创建:
2.DOM泄漏
下面有个很简单的一个例子,当我点击remove按钮删除test的时候,就会造成内存泄露。为什么呢?
原因就是删除的DOM在js中有全局的引用。也就是我删除的test在文中被引用,所以无法释放内存。所以在删除更新等操作后应该将其设置为null。
3.闭包
在my()内部创建的sendName()函数是不会被回收的,因为它被全局变量test引用,处于随时被调用的状态。如果向释放内存可以设置test=null;由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多。
4.被遗忘的计时器或回调函数
定时器要清除,内存才会被回收。回调函数也要赋null
网友评论