通过两个面试题解析JS底层原理

先来看一个例子:

setTimeOut(()=>{
    console.log('set1');
});
new Promise((resolve,reject)=>{
    console.log('p1')
  resolve();
}).then(()=>{
    console.log('then1');
})
console.log('1');

问:执行顺序?
答案:p1 --> 1 --> then1 --> set1 ;
new Promise() 的时候是同步执行。

微任务:

Promise,process.nextTick

宏任务:

整体的Scirpt代码,setTimeOut , setInterval

看看如下例子:


image.png

答案:pr1 ---> 2 ---> then1 ---> set1 --->then2 ---> then 4 ---> set2

解析过程:
确保微任务执行完了才去执行宏任务。

一 、js执行机制

image.png

另一个例子:
问:下面的代码执行会出现什么样的结果?

var b = [];
for(var i=0;i<15;i++){
    b.push(new Array(20*1024*1024));
}

结果,js内存不够了,V8引擎的内存溢出,崩溃了。


image.png

so,V8引擎内存有多大?


image.png

image.png

疑问:为什么64位的系统就只设置1.4G?设计的这么小?

原因:js回收的时候会中断执行。js回收100MB内存,则需要3ms。所以不能设计太大,因为会整个中断掉。

疑问:为什么1.4GB 是比较合适的?

原因:js的设计是为了跑前端脚本的,不是持续的,执行完了就没了。而不像后端,持续执行的,所以后端要大一些

,所以前端1.4GB是比较合适的。而且是足够了。

概念解释:

新生代:新变量存放的地方

老生代:没有被回收的老变量存放的地方

如果新生代要进行复制更改的话,就会被放到老生代里面去。算法:如果新生代的内存占用整个空间的25%的时候就会进行一次复制;

那么是如何进行复制的?

新生代有两个空间,当from空间大于25%的时候,会把from空间的活着的变量标记,并且复制到新生代To空间去。然后清空from空间。当To空间大于25%的时候,在此标记活着的变量,复制到from空间去。清空To。

啥时候新生代的变量会去老生代?

当新生代的空间大于25%,并且还活着的变量,复制过后,就去老生代空间。

二、内存如何回收?

image.png

当一个变量 既不是全局,也不是局部且失去引用的时候,就会被回收。

如何查看内存

浏览器:window.performance
Node:process.memoryUsage();
image.png

Node 查看内存:

打开终端(前提是你安装了node):

输入 node 进入node环境
oricess.memoryUsage()
会显示如下:
{
    rss:21258240,//总申请到的内存
    heapTotal:5656576,//总堆内存
    heapUsed:3051464,//已经使用的内存
    external:1401021//Node 和 js 不同的这个。因为node的源码是C++写的,可以支配一些内存给C++,所以会有一些额外的内存来给C++使用。
}

接下来我们做个小测试:

function getme(){
    var mem = process.memoryUsage();
    var format = function(bytes){
        return (bytes/1024/1024).toFixed(2)+"MB";
    }
    console.log("heapTotal:" + format(mem.heapTotal) + 'heapUsed:'+ format(mem.heapUsed));
}

var a =[];
var size = 20*1024*1024;
function b(){
    var arr1 = new Array(size);
    var arr2 = new Array(size);
    var arr3 = new Array(size);
    var arr4 = new Array(size);
    var arr5 = new Array(size);
}
b();
getme();
setInterval(()=>{
    a.push(new Array(20*1024*1024));
    getme();
},1000);

每一秒钟执行一次;我们看下执行结果:


image.png

image.png

直到最后就崩了。。

梳理下:

最开始的时候有800多M,因为我们定义了5个局部变量并且没被回收。当到达1400的时候,发现要回收,回收了上面5个数组,当我们到最后,再去回收,发现全局只有一个a变量,无法被回收,所以最后崩溃了。

//5个全局变量 如下:
var size = 20*1024*1024;
var arr1 = new Array(size);
var arr2 = new Array(size);
var arr3 = new Array(size);
var arr4 = new Array(size);
var arr5 = new Array(size);

三、容易引发内存使用不当的操作

image.png

1、反面教材1(滥用全局变量):

var size = 20*1024*1024;
var arr1 = new Array(size);
var arr2 = new Array(size);
var arr3 = new Array(size);
var arr4 = new Array(size);
var arr5 = new Array(size);
var arr6 = new Array(size);
var arr7 = new Array(size);
var arr8 = new Array(size);
var arr9 = new Array(size);
var arr10 = new Array(size);
var arr11 = new Array(size);
var arr12 = new Array(size);
var arr13 = new Array(size);
var arr14 = new Array(size);
var arr15 = new Array(size);

然后运行,直接报错,内存溢出。
如何正确的使用全局变量?

var size = 20*1024*1024;
var arr1 = new Array(size);
//使用完后释放
arr1 = undifined; //释放内存

额外小知识:

undifined 和 null 的区别:
null 是个关键字
undifined 是一个变量,和我们平时定义的 var a,bc 一样
image.png

我们测试如下代码:

var size = 20*1024*1024;
var arr1 = new Array(size);
arr1 = undifined;
var arr2 = new Array(size);
arr2 = undifined;
var arr3 = new Array(size);
arr3 = undifined;
var arr4 = new Array(size);
arr4 = undifined;
var arr5 = new Array(size);
arr5 = undifined;
var arr6 = new Array(size);
arr6 = undifined;
var arr7 = new Array(size);
arr7 = undifined;
var arr8 = new Array(size);
var arr9 = new Array(size);
var arr10 = new Array(size);
var arr11 = new Array(size);
var arr12 = new Array(size);
var arr13 = new Array(size);
var arr14 = new Array(size);
var arr15 = new Array(size);

运行后正常没有报错:


image.png

所以,尽量不要定义全局变量,如果一定要定义,那么定义完了,请释放。

2、反面教材2(缓存不限制):

缓存一定是全局的,如果不限制,早晚都会崩溃的。尤其是服务端,缓存一定要设置大小。
比较好的处理方式,给缓存加锁。

var b = [];
for(var i=0;i<15;i++){
    b.push(new Array(20*1024*1024));
}

运行后是崩溃的


image.png

改成如下:

var b = [];
for(var i=0;i<15;i++){
    if(b.length > 4){
        a.shift();  
    }
    b.push(new Array(20*1024*1024));
}

运行后正常,避免无限制的缓存造成内存崩溃。

缓存是你优化的好帮手,如果你一定要用的话,请在缓存上加一个锁。

3、反面教材3(操作大文件):

前端上传大文件,如果不处理 会直接卡死,

解决办法:切片上传;

使用node读取大文件的时候,如果是你使用

fs.readFile();  

将会一次性将大文件读取到内存中,文件较大会发生卡顿或者崩溃。

4、扩展知识点,老生代的回收算法:

新生代算法,标记活的复制到另一个空间去,牺牲空间,换取时间。

老生代则不可能,因为有1.4G,老生代则是,标记-删除-整理的操作。

新生代:标记活着的变量

老生代:标记死亡变量,当内存大了,删除死亡的变量。整理,

[ ,1002,, 100]//如果不整理,数组这样的就不会是连续的了,如果不经常整理,数组就没办法继续添加了。数组必须是连续的。通过整理变成[ 1002,100,,],连续的数组。

5、扩展额外的例子:

var a ={n:1};
var b=a;
a.x = a = {n:2};

console.log(a);
console.log(b);

问:a,b分别是什么值?

答案:


image.png

解析:

对象是引用类型的--> 所有的对象复制其实是给了这个对象的引用地址;

假设 {n:2} 的地址是 1002,{n:1} 的地址 1000

当var b = a 的时候,a,b的地址都是1000;

当a.x=a={n:2}的时候,先会执行a.x = a ,这时候x的地址是1000,即为a.x = {n:1,x:1000的地址};

a又等于{a:2},所以a的地址变成了1002;

所以打出来的 a = {a:2}, b={n:1,x:{n:2}};

关注我,学习更多前端原理
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容

  • 介绍JVM中7个区域,然后把每个区域可能造成内存的溢出的情况说明 程序计数器:看做当前线程所执行的字节码行号指示器...
    jemmm阅读 2,229评论 0 9
  • 所有知识点已整理成app app下载地址 J2EE 部分: 1.Switch能否用string做参数? 在 Jav...
    侯蛋蛋_阅读 2,424评论 1 4
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,106评论 0 8
  • 一、运行时数据区域 Java虚拟机管理的内存包括几个运行时数据内存:方法区、虚拟机栈、本地方法栈、堆、程序计数器,...
    加油小杜阅读 1,517评论 1 15
  • 早晨在晨间日记小组里打完卡,有一个姑娘叹气说:“情绪简直是我人生第一大问题,两个月调整到现在又回到原点了。我也不知...
    八子草铺阅读 181评论 0 1