内存管理
不管什么样的编程语言,在代码的执行过程中都是需要给它分配内存,且内存的管理都会有如下的生命周期:
- 分配申请你需要的内存(申请);
- 使用分配的内存(存放一些东西,比如对象等);
- 不需要使用时,对其进行释放;
js内存管理
- JS对于基本数据类型内存的分配会在执行时,直接在栈空间进行分配;
- JS对于复杂数据类型内存的分配会在堆内存中开辟一块空间,并且将这块空间的指针返回值变量引用;
垃圾回收
现代的编程语言都是有自己的垃圾回收机制 常见的GC算法:
- 引用计数:
- 当一个对象有一个引用指向它时,那么这个对象的引用就+1,当一个对象的引用为0时,这个对象就可以被销毁掉;
- 这个算法有一个很大的弊端就是会产生循环引用;
- 标记清除:
- 这个算法是设置一个根对象(root object),垃圾回收器会定期从这个根开始,找所有从根开始有引用到的对象,对于哪些没有引用到的对象,就认为是不可用的对象。
- JS引擎比较广泛的采用的就是标记清除算法,但是类似于V8引擎为了进行更好的优化,它在算法的实现细节上也会结合一些其他的算法。
js代码执行相应的内存变化
JS代码执行过程
初始化全局对象
js引擎在执行代码之前,会在堆内存中创建一个全局对象:Global Object(GO),里面会包含Date、Array、String、Number、setTimeout、setInterval等等;其中还有一个window属性指向自己。
执行上下文栈(调用栈)
- js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈。
调用栈首先会执行全局代码块
- 全局的代码块为了执行会构建一个Global Execution Context(GEC)
- 然后GEC会被放入到ECS中执行;
GEC执行前后
- 执行前:在代码执行前,在parser转成AST的过程中,会将全局定义的变量、函数等加入到GlobalObject中,但是并不会赋值;这个过程也称之为变量的作用域提升(hoisting)
- 执行过程:对变量赋值,或者执行其他的函数;
函数执行
在执行的过程中执行到一个函数时,就会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC),并且压入到EC Stack中。
FEC执行前后
执行前:
- 在解析函数成为AST树结构时,会创建一个Activation Object(AO):AO中包含形参、arguments、函数定义和指向函数对象、定义的变量;
- 第二部分:作用域链:由VO(在函数中就是AO对象)和父级VO组成,查找时会一层层查找;
- 第三部分:this绑定的值。
执行过程: 对变量赋值,或者执行其他的函数;
执行后: 如果函数不构成闭包,则AO对象会被释放掉,调用栈中FEC也会清空。
-- 文章参考coderwhy老师的课程总结