浅谈js的编译与运行
垃圾回收机制是面试官常考的问题,我在摔了几次跟头后决定认真写一篇文章来疗疗!!言归正传,想要深入了解javascript这门编程语言,垃圾回收机制是必须要了解清楚的,这对于我们软件开发是至关重要的,可以优化内存。我先举一个例子来感受一下吧!!
js
function foo(){
var a=1
var b=a
a=2
console.log(a)
console.log(b)
}
foo()
显然这段代码打印出来的结果为2,1。接下来就由我来为大家深入剖析一下吧!!这份代码在被v8引擎读取的那一刻要先进行编译然后在执行,预编译的时候会创建函数上下文执行对象。在v8引擎工作的那一刻一定会有一个全局上下文对象入栈。其实能被v8放入调用栈里边的东西一定是能形成作用域的,全局上下文对象它本身有作用域因此会被放入调用栈。好了,言归正传我们来继续剖析上述代码。全局上下文对象入栈后,我们来找一下全局申明的变量 foo(只有一个foo),此时由于foo是一个函数体因此要开辟出一个堆结构来存放 函数体这类的引用类型,然后在把这个函数体对应的指针存放在全局上下文当中。此时全局编译结束,编译就是找函数申明,变量申明。接下来就开始全局的执行,执行的话就带来foo的调用。然后foo的执行上下文入栈(分为词法环境,和变量环境),然后就找变量申明。a,b。由于a,b是原始类型,因此会被放入栈里边。顺便提一嘴,由于引用类型如:对象,函数,数组......这是内存很大的,因为拿数组举例,它这里边可能存放了几千几万个数据,那这就会很大。如果把它放入栈当中就会出现爆栈的情况。因此把它放入堆当中,有人会想为什么不把调用栈设计的大一点,其实这样子想也没错,但如果调用栈无底线的让你把上下文入栈的话,那么到时候v8执行的效率会很低,所以v8不能设计的很大。上边可转化成图形如下!!
我们来看看另外一段代码
js
function foo(){
var a={name:'fu'}
var b=a
a.name='牛哥'
console.log(a);
console.log(b);
}
foo()
打印结果为{ name: '牛哥' } { name: '牛哥' }
且看试图~
js特点
顺便提一点js这门语言的特点,js是一门弱类型,动态语言。体现在如下: 1.弱类型体现在:声明变量时候不需要告诉v8该变量是什么类型,v8会自己计算出来。 2.动态体现在:可以使用同一个变量保存不同类型的数据。
垃圾回收
手动
js
char *p=(char*)malloc(2048)
//
free(p)
p=null
在c或c++中我们可以使用free(p)来进行手动回收。
自动回收
栈内存回收
在js java python中有自动回收机制。
js
function foo(){
var a=1
var b={name:'fu'}
function showName(){
var c=2
var d={name:'牛哥'}
}
showName()
}
foo()
我们可以看到先是全局上下文入栈,然后是foo()入栈,最后是showName入栈。 同时呢调用栈有一个当前执行状态的指针(ESP),showName被调用的那一刻,调用栈中的ESP指针指向的是showName的执行上下文。showName执行完毕后就指向foo函数的执行上下文。这时候就会销毁指针走过的对象。这是栈内存就回收啦!!
流程图如下
堆内存回收
栈内存能够回收,那么我们之前的函数体,对象是放在堆当中这又是怎么回收的呢??存在着一个垃圾回收器。垃圾回收器的工作流程如下: 1.标记空间中的活动对象和非活动对象 2.回收掉非活动对象的内存 3.内存整理(把不连续的整理成连续的)
代际假说
1.假说认为大部分对象在内存中存在的时间是很短的 2.如果对象存在时间很长,那么就是不死的对象。会活得非常久,
v8的堆
v8的堆分为两个区域一个是新生代区域,另一个是老生代区域。 新生代区域(1~8M 副垃圾回收器)呢是存放生存时间短的对象,老生代区域(主垃圾回收器)是存放生存时间长的对象或者内存很大的对象。1.新生代区域先是划分对象区域和空闲区域,然后将对象区域中的活动对象复制到空闲区域,清空对象区域最后反转对象区域和空闲区域。两次之后还存活的对象就晋升到老生代区域。2.老生代区域呢就是递归标记每一个对象中存活的子对象,清理掉失活的对象,整理内存。 流程图如下:
全停顿
当垃圾回收算法生效的时候,js逻辑的执行就要停止,等待垃圾回收完成后再执行。 我们试想一下这个场景:主线程在运行我们的代码这时候垃圾回收机制开始工作先标记再整理再清理。假设ya