100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > JS引擎中垃圾回收机制

JS引擎中垃圾回收机制

时间:2023-08-14 17:45:46

相关推荐

JS引擎中垃圾回收机制

思考:

1.JavaScript的内存是如何管理的?

2.Chrom浏览器是如何进行垃圾回收的?

我们带着以上两个思考来阅读文章。

一、JS内存管理

MDN介绍:“JavaScript 是在创建变量(对象,字符串等)时自动进行了分配内存,并且在不使用它们时“自动”释放。 释放的过程称为垃圾回收。这个“自动”是混乱的根源,并让 JavaScript(和其他高级语言)开发者错误的感觉他们可以不关心内存管理。”

内存管理大致分为3个部分:

1.分配你所需要的内存空间,在创建变量时zi'd

2.使用分配到的内存空间进行读写操作等

3.不需要使用内存时,将其空间释放或者归还

JavaScript大致将数据类型分为两个类别,基本类型数据和引用类型数据:

基本数据类型:在内存中占据固定的内存空间,值保存在栈空间中,直接可以通过值来访问这些。引用类型:由于引用类型值大小不固定,栈内存中存放地址中的的内存对象,通过引用(地址)来访问的。

var a=123 //给数值变量分配栈内存空间var etf = 'ARK' // 给字符串也是分配栈内存空间// 给对象及其值分配堆内存var obj = {name:'TOM',age:13}// 给数组及其值分配内存(和对象一样)var a = [1, null]// 给函数分配堆内存function sum(a,b){return a+b}

栈内存中的基本数据类型可以通过操作系统直接处理,而堆内存中的引用数据类型,正是由于可以经常变化,大小不固定,因此需要JavaScript的引擎通过垃圾回收机制来处理。

二、垃圾回收

大多数内存管理问题都在这个都在这个阶段,如何判断哪些内存不再需要使用了要进行回收呢?

普通的理解方法参考MND:“垃圾回收机制有两种方式,一种是引用计数垃圾收集,一种是标记-清除算法”

1.引用

垃圾回收算法主要依赖于引用的概念。在内存环境中,一个对象如果具有访问另一个对象的权限就称为引用,分为显示显示引用和隐式引用。一个Javascript对象具有对它原型的引用叫做隐式引用,对它的属性的引用称为显示引用。

引用计数垃圾收集:

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

var o = {a: {b:2}};// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量 o// 很显然,没有一个可以被垃圾收集var o2 = o; // o2 变量是第二个对“这个对象”的引用o = 1;// 现在,“这个对象”只有一个 o2 变量的引用了,“这个对象”的原始引用 o 已经没有var oa = o2.a; // 引用“这个对象”的 a 属性// 现在,“这个对象”有两个引用了,一个是 o2,一个是 oao2 = "yo"; // 虽然最初的对象现在已经是零引用了,可以被垃圾回收了// 但是它的属性 a 的对象还在被 oa 引用,所以还不能回收oa = null; // a 属性的那个对象现在也是零引用了// 它可以被垃圾回收了

❌循环引用

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

function f(){var o = {};var o2 = {};o.a = o2; // o 引用 o2o2.a = o; // o2 引用 oreturn "azerty";}f();

容易造成内存泄漏的问题。

2.标记-清除算法

这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。这个算法里假定设置一个(root)的对象(全局对象)垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。

很好地解决了循环引用问题,在上面的示例中,函数调用返回之后,两个对象从全局对象出发无法获取。因此,他们将会被垃圾回收器回收。第二个示例同样,一旦 div 和其事件处理无法从根获取到,他们将会被垃圾回收器回收。

// 可达var name = 'Tom'var obj = {arr: [1, 2, 3]}console.log(window.name) // Tomconsole.log(window.obj) // { arr: [1, 2, 3] }console.log(window.obj.arr) // [1, 2, 3]console.log(window.obj.arr[2]) // 3function fn () {var age = 22}// 不可达console.log(window.age) // undefined

❌:那些无法从根对象查询到的对象都将被清除

尽管这是一个限制,但实践中我们很少会碰到类似的情况,所以开发者不太会去关心垃圾回收机制

以上这些理解往往是不够的,请继续往下学习:

三、V8垃圾回收机制

Javascript中的V8引擎被限制了内存使用((64位约1.4G/1464MB , 32位约0.7G/732MB)),根据不同的操作系统内存大小也会有所不同。

为什么限制V8内存大小呢?

V8最开始是为浏览器而设计的引擎并未考虑占用过多的内存空间由于被V8的垃圾回收机制所限制,这样会引起Javascript执行的线程被挂起会影响当前执行页面的性能

1. 分代回收

1.1 新生代内存回收

在64位操作系统下分配为32MB,因为新生代中变量的存活时间短,不太容易产生太大的内存压力,因此不够大也是可以理解的。

图中左边是正在使用的内存空间,右边是闲置的内存空间。V8引擎进行垃圾回收时,会将左边空间中的对象进行检查,会将左边存活的活动对象复制到右边空间,并且将这些对象的内存有序地排列起来,然后将From-space中的非活动对象进行释放,完成之后将from-space和to-space进行互换,这样可以使得新生代中的这两块区域重复利用。如果是顺序放置的比较好处理。

图中橙色方便是存活的内存对象,白色是未分配的内存空间,接下来将介绍一种算法来解决这个问题,Scavenge算法,适用于内存比较小的情况,下图是经过算法处理后,内存空间的排布。

介绍Scavenge算法:

Scavenge算法是一种copy算法,copy存活的对象到to-space中

1.2 老生代内存回收

新生代中的变量如果经过回收后依然一直存在,那么就会直接被放入老生代内存中,只要是已经经历过一次Scanvenge算法回收的就可以晋升未老生代内存中的对象。老生代中就不适合用Scavenge算法了,在老生代中采用Mark-Sweep(标记-清除)和Mark-Compact(标记-整理)的策略来进行老生代内存回收。

标记阶段和清除阶段:

首先它会遍历堆上的所有对象,分别对它们打上标记,然后在代码执行结束后,对使用过的变量取消标记。在清除阶段,就会把还有标记的进行整体清除,从而释放内存空间。

但是通过标记清除后,还是会存在内存碎片问题。内存碎片多了之后,如果要新来一个较大的内存对象需要存储,会造成影响对于通过标记清除产生的内存碎片,还是需要通过标记整理策略进行解决。

标记整理(Mark-Compact):

每次清理完非活动对象,就会把剩下的活动对象,整理到内存的一侧,整理完成后,直接回收掉边界上的内存。

参考:内存管理 - JavaScript | MDN

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。