JS内存泄漏与垃圾回收机制

tech2022-07-05  237

**执行环境定义了变量或者函数有权访问的其他数据,决定了他们各自的行为。**每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量的函数都保存在这个对象中。虽然我们编写的代码无法访问到这个对象,但是解析器在处理数据会在后台调用它。

全局执行环境被认为时window对象。

什么是内存泄漏?

程序的运行需要内存,只要程序提出要求,操作系统或者运行是就必须供给内存。

对于持续运行的服务进程,必须及时释放内存,否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。

不再用到的内存,没有及时释放,就叫做内存泄漏。

有些语言(比如c语言)必须手动释放内存,程序员负责内存管理。

这很麻烦,所以大多数语言提供自动内存管理,减轻程序员的负担,这被称为"垃圾回收机制"。

javascript垃圾回收机制原理:

解决内存的泄露,垃圾回收机制会定期(周期性)找出那些不再用到的内存(变量),然后释放其内存。

现在各大浏览器通常采用的垃圾回收机制有两种方法:标记清除,引用计数。

标记清除:

js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在一个函数中声明一个变量,就将这个变量标记为"进入环境",从逻辑上讲,永远不能释放进入环境变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为"离开环境"。

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

垃圾回收机制在运行的时候 会给存储在内存中的所有变量都加上标记(可以是任何标记方式),然后,它会去掉处在环境中的变量及被环境中的变量引用的变量标记(闭包)。而在此之后剩下的带有标记的变量被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后垃圾回收机制到下一个周期运行时,将释放这些变量的内存,回收它们所占用的空间。

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

引用计数:

语言引擎有一张"引用表",保存了内存里面所有资源(通常是各种值)的引用次数。 引用计数的含义是:跟踪记录每一个值被引用的次数。 当声明了一个变量 并将 一个引用类型值赋给该变量时,则这个值的引用次数就是1. 如果同一个值又被赋给另一个变量,则该值的引用次数加1; 相反,如果包含这个值引用的变量又取得了另外一个值,则该值的引用次数减1; 如果一个值的引用次数是0,就表示这个值不再用到了,因此可以将这块内存释放。 上图中,左下角的两个值,没有任何引用,所以可以释放。

如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。

const arr = [1,2,3,4]; console.log("hello world");

上面的代码中,数组[1,2,3,4]是一个值,会占用内存。变量arr是仅有的对这个值的引用,因此引用次数为1。尽管后面的代码没有用到arr,它是会持续占用内存。

如果增加一行代码,解除arr对[1,2,3,4]引用,这块内存就可以被垃圾回收机制释放了。

let arr = [1,2,3,4]; console.log("hello world"); arr = null;

上面代码中,arr重置为null,就解除了对[1,2,3,4]的引用,引用次数变成了0,内存就可以释放出来了。

因此,并不是说有了垃圾回收机制,程序员就轻松了。你还是需要关注内存占用:那些很占空间的值, 一旦数据不再用到,最好通过将其设置成null来释放其引用,这个做法就叫解除引用。 解除引用真正的作用是:让值脱离执行环境,以便垃圾收集器下次运行时将其回收。

内存泄漏的识别方法

怎样可以观察到内存泄漏呢?

经验法则是,如果连续五次垃圾回收之后,内存占用一次比一次大,就有内存泄漏。这就要求实时查看内存占用。

最新回复(0)