您的位置:首页 > Web前端 > Vue.js

Vue 技术栈 教你玩"坏" v8引擎 吃透 js 内存回收机制

2020-04-07 18:32 1471 查看

写在开头

学习完了ES 6基础,推荐阅读:ECMAScript 6 全套学习目录 整理 完结

现在开始逐步深入Vue 技术栈,想了想,技术栈专栏的主要内容包括:

1、Vue源码分析
2、手把手教 保姆级 撸代码
3、无惧面试,学以致用,继承创新
4、谈谈前端发展与学习心得
5、手写源码技术栈,附上详细注释
6、从源码中学习设计模式,一举两得
7、编程思想的提升及代码质量的提高
8、通过分析源码学习架构,看看优秀的框架
9、项目实战开发
10、面试准备,完善个人简历

暂时想到的就这么多,把这列举的10点做好了,我觉得也OK了,欢迎一起学习,觉得不错的话,可以关注博主,专栏会不断更新,可以关注一下,传送门~

学习目录

为了方便自己查阅与最后整合,还是打算整个目录,关于Vue技术栈前面的几篇优秀的文章:

Vue 技术栈 手写响应式原理 到 探索设计模式

文章目录

  • v8是如何处理变量的
  • 如何注意内存的使用
  • 总结
  • 正文

    理解内存的意义

    • 防止页面占用内存过大,引起客户端卡顿,甚至无响应
    • node使用的也是v8引擎,内存对于后端服务的性能至关重要。因为服务的持久性,后端更容易造成内存溢出
    • 在面试官前秀一波操作,加深对你的印象,加分

    v8引擎内存回收机制

    v8的内存分配
    内存大小
    • 和操作系统有关64位为1.4G,32位为0.7G
    • 64位下新生代的空间为64MB,老生代为1400MB
    • 32位下新生代的空间为16MB,老生代为700MB

    why?

    为什么内存大小要这样设计呢,读者可以先思考一下。

    就现在来说的话,一般我们家用台式电脑、尤其是笔记本电脑,内存一般是4G或8G,当然有需求的人会去拓展,内存有这么大,为什么v8引擎不把内存限制提高一点呢?多一点好像也不成问题吧?

    解惑:js作为一个脚本语言,设计之初就是为了服务浏览器,而浏览器作为前端有一个特点就是

    不持久化

    因此,js代码的特点是执行一遍,几乎全部回收了,不像后端那样开启某一个服务,所有定义的全局变量一直存在。理论上说,1.4G已经够用了,俗话说:“杀鸡焉用宰牛刀”就是这个道理。

    另外一个原因,就是js回收内存的时候,会

    暂停执行

    var a=l;
    var b=2;  //假设在执行定义b变量时发生了内存回收
    //暂停执行
    c();

    回收一次100MB内存,大概需要6ms

    当回收内存达到1GB时,大概需要1s的时间

    因此,js的执行将会暂停1s,所以内存设置越大,可能执行一下就会卡顿一下,这肯定会影响用户的体验。(关于暂停执行这一块,下文会详细举例说明)

    垃圾回收算法

    我尽量用易懂的语言来讲解该算法的实现机制,关于具体算法的逻辑实现这里就不做详细探索了,有兴趣的读者可以开拓一下,引用优秀的文章。

    • 新生代简单来说就是先复制、用完直接删除
    • 老生代就是先标记删除、然后再整理

    通过看图,我们能发现,新生代分成了两块。新生代内存空间是用来存放新产生的变量,一般是比较小,存在时间短的变量,而老生代如字面意思说存放新生代中旧的变量。

    最开始,新的变量会放在

    From
    块中,当满足一定条件(下文会有介绍)后,就会将有用的变量复制一份到
    To
    块中,然后把
    From
    块全部清空。之后From变成了To,To变成了From,又将To块全部清空(可能这里有点绕,读者可以放慢阅读理解一下),然后就这样交替着将有用的变量进行转移。

    那么,为什么在新生代要进行分块复制呢?学习过算法的读者应该知道,其中有两个重要指标:时间复杂度和空间复杂度,也是衡量一个算法优劣的标准,通过分块复制,可以用空间来换取时间,来对我们的算法进行优化。比如上述图示,当

    From
    有新变量时,
    To
    块是空着的,反过来,当
    To
    有新变量时,
    From
    块是空着的。

    对于老生代,又采用了新的算法,因为新生代虽然牺牲了一般的空间,但是它总空间本来就不是很多,而老生代的空间要多好几倍,如果依旧采用该算法的话,那就很大程度地造成了资源的浪费,这并不是我们想要的结果。

    老生代问题

    由上图,来讲解一下关于老生代的问题,这里假设黑色部分是被标记需要删除的变量,那么在回收的时候会进行一个类似栈的操作,将需要删除的部分弹出去之后,后面的部分进行压栈进去。简单来说,就是黑色删除之后,白块往前挪动,这也是之前提及到的

    整理
    操作。这里类似于对电脑进行磁盘碎片整理的操作。 这样操作,也是为了保证内存连续性。

    于是,就有了新的问题,为什么我们要保证内存的连续性

    表面现象上内存感觉会更多了,但是深层去考虑的话一个原因是数组必须是连续的内存空间

    //数组必须是连续的内存空间
    var arr=[1,2,3,4];
    假设连续内存:1000,1001,1002,1003

    这里就好像我们一起去网吧开黑打撸一样,5个人当然是去找5排的位置…

    新生代如何变成了老生代

    上文,我们简单提及到了新生代当满足某一特定条件后就会成为老生代,下面我们进行详细探索:

    通过上文,我们知道新生代多存放的是临时变量,如下左图所示,① 如果这个变量被回收过,那么就会晋升成为

    老生代
    。如下右图所示,② 看To空间是否使用了25%(以64位为例的话就是32MB的25%),因此一些占用内存比较大的变量会直接丢在
    老生代
    里去。

    v8是如何处理变量的

    理解如何处理内存,其实说白了就是理解如何处理我们的变量。

    利用node来查看内存使用情况

    关于内存使用情况,在浏览器上面,也是可以直接查看的,按F12,在控制台输入以下内容:

    window.performance

    就能看到我们的内存占用了

    另外,你可以可以通过Performance选项进行查看

    但我们肯定不会总是去浏览器去查看我们的内存使用情况的,这里推荐使用node调用api来查看我们的内存使用情况

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

    以window10为例,进入我们node环境,输入

    process.memoryUsage()
    ,查看下图,它会返回给我们一个对象,包含以下内容:

    • heapTotal 总内存
    • heapUsed 已使用内存
    • external 额外内存

      这里进行拓展一下,有学生说自己的内存拓展到了4G,其实拓展的并不是v8的内存,而是C++的内存

    拓展知识:

    node是C++写的,因此它有分配C++内存的能力。那么你可以给你的项目进行扩容,不过前提条件是node环境,浏览器的环境是不可以的

    变量处理

    变量主要分为两大类,全局变量和局部变量。

    • 内存主要就是存储变量等数据的
    • 全局对象会始终存活到程序运行结束
    • 局部变量当程序执行结束,且没有引用的时候就会消失

    谈谈关于闭包问题:

    疑问点:闭包会不会消失?

    如果你使用了闭包,而且还一直处于引用状态,那么它就不会消失

    疑问点:闭包会引起内存回收,让闭包内的变量一直得不到回收,造成了内存泄露?

    这个说法其实是错误的,这个问题是IE5时代的一个bug,v8引擎一直在进步,基本上不存在这个问题。或许你是看了一本《JavaScript权威指南》那一本书,但是出版的挺早的了,那个时候还是IE的时代,所以闭包引起内存回收几乎不存在了,甚至你可以用闭包来消除全局变量。

    教你如何玩"坏"v8引擎(起飞)

    如下,我们定义了15个全局变量,每一个20MB,想想就会内存溢出,那么后面输出还

    能输出吗?

    var size=20*1024*1024;	//20MB
    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);
    console.log('能输出吗?');

    执行:

    首先,当我们输入指令后,还会卡几秒钟

    读者也可以仔细找找,看看到底有没有输出我们想要的结果

    <--- Last few GCs --->
    
    [77876:000001F7424EADC0]     7482 ms: Mark-sweep 2081.4 (2114.3) -> 2081.4 (2083.3) MB, 2053.8 / 0.0 ms  (average mu = 0.124, current mu = 0.000) last resort GC in old space requested
    [77876:000001F7424EADC0]     8645 ms: Mark-sweep 2081.4 (2083.3) -> 2081.4 (2083.3) MB, 1163.0 / 0.0 ms  (average mu = 0.074, current mu = 0.000) last resort GC in old space requested
    
    <--- JS stacktrace --->
    
    ==== JS stack trace =========================================
    
    0: ExitFrame [pc: 00007FF7CD2E2C6D]
    1: ConstructFrame [pc: 00007FF7CD25DF0A]
    2: StubFrame [pc: 00007FF7CD343A90]
    Security context: 0x02b1829008a1 <JSObject>
    3: /* anonymous */ [000002B18292DAA1] [G:\??????\Vue??\????\me.js:26] [bytecode=000002B1829307B9 offset=198](this=0x02b18292dbd1 <Object map = 0000018E79440431>,0x02b18292dbd1 <Object map = 0000018E79440431>,0x02b18292db91 <JSFunction require (sfi = 000002B182930E41)>,...
    
    FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
    1: 00007FF7CC71094F napi_wrap+124431
    2: 00007FF7CC6B2696 v8::base::CPU::has_sse+34502
    3: 00007FF7CC6B3356 v8::base::CPU::has_sse+37766
    4: 00007FF7CCEB6F4E v8::Isolate::ReportExternalAllocationLimitReached+94
    5: 00007FF7CCE9EF91 v8::SharedArrayBuffer::Externalize+833
    6: 00007FF7CCD6C85C v8::internal::Heap::EphemeronKeyWriteBarrierFromCode+1436
    7: 00007FF7CCD68890 v8::internal::Heap::AddRetainedMap+2608
    8: 00007FF7CCD8289E v8::internal::Factory::AllocateRawFixedArray+94
    9: 00007FF7CCD89C34 v8::internal::Factory::NewFixedArrayWithFiller+52
    10: 00007FF7CCD89BF1 v8::internal::Factory::NewFixedArray+65
    11: 00007FF7CCC5A98F v8::internal::FeedbackNexus::ic_state+56767
    12: 00007FF7CCC6C6C5 v8::Message::GetIsolate+14101
    13: 00007FF7CCC78600 v8::Message::GetIsolate+63056
    14: 00007FF7CCC5438C v8::internal::FeedbackNexus::ic_state+30652
    15: 00007FF7CCB1E8AA v8::internal::OrderedHashMap::ValueAt+62122
    16: 00007FF7CD2E2C6D v8::internal::SetupIsolateDelegate::SetupHeap+567949
    17: 00007FF7CD25DF0A v8::internal::SetupIsolateDelegate::SetupHeap+23850
    18: 00007FF7CD343A90 v8::internal::SetupIsolateDelegate::SetupHeap+964784
    19: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    20: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    21: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    22: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    23: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    24: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    25: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    26: 00007FF7CD25FDB1 v8::internal::SetupIsolateDelegate::SetupHeap+31697
    27: 00007FF7CD25F99C v8::internal::SetupIsolateDelegate::SetupHeap+30652
    28: 00007FF7CCDC4F43 v8::internal::Execution::CallWasm+1395
    29: 00007FF7CCDC48C6 v8::internal::Execution::Call+182
    30: 00007FF7CCE95B3B v8::Function::Call+603
    31: 00007FF7CC6D95CE node::Start+1150
    32: 00007FF7CC6D9877 node::Start+1831
    33: 00007FF7CC6D874A node::LoadEnvironment+26
    34: 00007FF7CC67B715 EVP_CIPHER_CTX_buf_noconst+30565
    35: 00007FF7CC6D9263 node::Start+275
    36: 00007FF7CC59666C RC4_options+339308
    37: 00007FF7CD395D58 v8::internal::SetupIsolateDelegate::SetupHeap+1301368
    38: 00007FFC37D77BD4 BaseThreadInitThunk+20
    39: 00007FFC3864CEE1 RtlUserThreadStart+33

    很明显,一个中文都没有,可能在13或者14样子的时候,内存不够了,然后想着去回收,但是发现没有变量能够回收,直接炸掉…

    为了让读者更清晰看到里面隐藏的机制,我们边执行,边打印一下:

    function getme(){
    var mem = process.memoryUsage();
    var format = function(bytes){
    return (bytes/1024/1024).toFixed(2)+'MB';
    };
    console.log('Process: heapTotal '+format(mem.heapTotal) +
    ' heapUsed ' + format(mem.heapUsed) +
    ' rss ' + format(mem.rss)
    );
    };
    var size=20*1024*1024;	//20MB
    var arr1=new Array(size);
    getme();
    var arr2=new Array(size);
    getme();
    var arr3=new Array(size);
    getme();
    var arr4=new Array(size);
    getme();
    var arr5=new Array(size);
    getme();
    var arr6=new Array(size);
    getme();
    var arr7=new Array(size);
    getme();
    var arr8=new Array(size);
    getme();
    var arr9=new Array(size);
    getme();
    var arr10=new Array(size);
    getme();
    var arr11=new Array(size);
    getme();
    var arr12=new Array(size);
    getme();
    var arr13=new Array(size);
    getme();
    var arr14=new Array(size);
    getme();
    var arr15=new Array(size);
    getme();
    console.log('能输出吗?');
    

    会打印如下结果:

    Process: heapTotal 164.02MB heapUsed 161.97MB rss 177.51MB
    Process: heapTotal 325.27MB heapUsed 322.19MB rss 338.86MB
    Process: heapTotal 487.53MB heapUsed 482.22MB rss 499.32MB
    Process: heapTotal 651.53MB heapUsed 642.19MB rss 659.49MB
    Process: heapTotal 819.54MB heapUsed 802.19MB rss 819.87MB
    Process: heapTotal 995.54MB heapUsed 962.19MB rss 980.53MB
    Process: heapTotal 1155.54MB heapUsed 1122.20MB rss 1140.53MB
    Process: heapTotal 1315.55MB heapUsed 1282.20MB rss 1300.54MB
    Process: heapTotal 1475.55MB heapUsed 1442.20MB rss 1460.54MB
    Process: heapTotal 1635.55MB heapUsed 1602.20MB rss 1620.55MB
    Process: heapTotal 1795.56MB heapUsed 1762.20MB rss 1780.56MB
    Process: heapTotal 1955.56MB heapUsed 1922.20MB rss 1940.69MB
    Process: heapTotal 2115.57MB heapUsed 2081.72MB rss 2100.77MB
    
    <--- Last few GCs --->
    
    [79732:000001A9471C5D00]     5916 ms: Mark-sweep 2081.7 (2115.1) -> 2081.6 (2084.1) MB, 1132.9 / 0.0 ms  (average mu = 0.028, current mu = 0.000) last resort GC in old space requested
    [79732:000001A9471C5D00]     6957 ms: Mark-sweep 2081.6 (2084.1) -> 2081.6 (2084.1) MB, 1041.3 / 0.0 ms  (average mu = 0.015, current mu = 0.000) last resort GC in old space requested
    
    <--- JS stacktrace --->
    
    ==== JS stack trace =========================================
    
    0: ExitFrame [pc: 00007FF7CD2E2C6D]
    1: ConstructFrame [pc: 00007FF7CD25DF0A]
    2: StubFrame [pc: 00007FF7CD343A90]
    Security context: 0x01178b3408a1 <JSObject>
    3: /* anonymous */ [000003AE3E898171] [G:\??????\Vue??\????\me.js:39] [bytecode=000002CAAA69A049 offset=243](this=0x03ae3e8982a1 <Object map = 0000025C5EA80431>,0x03ae3e8982a1 <Object map = 0000025C5EA80431>,0x03ae3e898261 <JSFunction require (sfi = 000002CAAA69A749)>,...
    
    FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
    1: 00007FF7CC71094F napi_wrap+124431
    2: 00007FF7CC6B2696 v8::base::CPU::has_sse+34502
    3: 00007FF7CC6B3356 v8::base::CPU::has_sse+37766
    4: 00007FF7CCEB6F4E v8::Isolate::ReportExternalAllocationLimitReached+94
    5: 00007FF7CCE9EF91 v8::SharedArrayBuffer::Externalize+833
    6: 00007FF7CCD6C85C v8::internal::Heap::EphemeronKeyWriteBarrierFromCode+1436
    7: 00007FF7CCD68890 v8::internal::Heap::AddRetainedMap+2608
    8: 00007FF7CCD8289E v8::internal::Factory::AllocateRawFixedArray+94
    9: 00007FF7CCD89C34 v8::internal::Factory::NewFixedArrayWithFiller+52
    10: 00007FF7CCD89BF1 v8::internal::Factory::NewFixedArray+65
    11: 00007FF7CCC5A98F v8::internal::FeedbackNexus::ic_state+56767
    12: 00007FF7CCC6C6C5 v8::Message::GetIsolate+14101
    13: 00007FF7CCC78600 v8::Message::GetIsolate+63056
    14: 00007FF7CCC5438C v8::internal::FeedbackNexus::ic_state+30652
    15: 00007FF7CCB1E8AA v8::internal::OrderedHashMap::ValueAt+62122
    16: 00007FF7CD2E2C6D v8::internal::SetupIsolateDelegate::SetupHeap+567949
    17: 00007FF7CD25DF0A v8::internal::SetupIsolateDelegate::SetupHeap+23850
    18: 00007FF7CD343A90 v8::internal::SetupIsolateDelegate::SetupHeap+964784
    19: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    20: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    21: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    22: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    23: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    24: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    25: 00007FF7CD2627FC v8::internal::SetupIsolateDelegate::SetupHeap+42524
    26: 00007FF7CD25FDB1 v8::internal::SetupIsolateDelegate::SetupHeap+31697
    27: 00007FF7CD25F99C v8::internal::SetupIsolateDelegate::SetupHeap+30652
    28: 00007FF7CCDC4F43 v8::internal::Execution::CallWasm+1395
    29: 00007FF7CCDC48C6 v8::internal::Execution::Call+182
    30: 00007FF7CCE95B3B v8::Function::Call+603
    31: 00007FF7CC6D95CE node::Start+1150
    32: 00007FF7CC6D9877 node::Start+1831
    33: 00007FF7CC6D874A node::LoadEnvironment+26
    34: 00007FF7CC67B715 EVP_CIPHER_CTX_buf_noconst+30565
    35: 00007FF7CC6D9263 node::Start+275
    36: 00007FF7CC59666C RC4_options+339308
    37: 00007FF7CD395D58 v8::internal::SetupIsolateDelegate::SetupHeap+1301368
    38: 00007FFC37D77BD4 BaseThreadInitThunk+20
    39: 00007FFC3864CEE1 RtlUserThreadStart+33

    如上述代码所述,当内存定义到了第13个全局变量时,此时已经定义到了2GB左右多内存,发现后续还有变量需要开辟空间,于是想着要去回收一部分内存,此时发现之前全是全局变量,又炸了,因为

    全局变量会始终存活到程序运行结束
    ,于是乎,只能报个错——“我太南了…”

    这次就不玩坏它了,再玩可能心态就炸了,hhh。那么接下来看看是否如我们所想,会有一个回收的机制。从上文代码我们知道了,最多定义13个,不然会炸掉,那我们试试如下代码看看:

    function getme(){
    var mem = process.memoryUsage();
    var format = function(bytes){
    return (bytes/1024/1024).toFixed(2)+'MB';
    };
    console.log('Process: heapTotal '+format(mem.heapTotal) +
    ' heapUsed ' + format(mem.heapUsed) +
    ' rss ' + format(mem.rss)
    );
    };
    var size=20*1024*1024;	//20MB
    var a=[];
    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();
    for(var i=0;i<13;i++){
    a.push(new Array(size));
    getme();
    }
    console.log('能输出吗?');
    

    执行结果:

    分析一下:

    对于局部变量呢,并不是说用完了一定就会回收,只是可以被回收

    如上图执行结果所示,第一行输出了834.54MB,这里是定义全局变量的开始,但起初并不是为0,代表我们的局部变量并没有回收,那什么时候回收呢?我们看看下图:

    诶,这里突然内存就变小了,显然就是我们局部变量被回收的时刻,因为此时已经达到了1.9Gb左右了,按上文看的话,2.1GB左右就会炸掉,于是乎,v8引擎就会往前看看有没有可回收的变量。然后就将没有引用过的局部变量收回了,防止内存炸掉。

    由于这里不太方便演示输出过程,其实在内存回收的时候其实会有一点卡顿的感觉,这是必然的,为什么?

    其实上文已经提及过,js回收时会暂停执行,于是会有一点卡顿的感觉,这里读者可以去实践体会一下。

    如何注意内存的使用

    优化内存的技巧
    • 尽量不要定义全局变量
    • 全局变量切记要销毁掉
    • 用匿名自执行函数变全局为局部
    • 尽量避免过多的引用闭包

    回到之前的代码问题,定义了这么多的全局变量,内存肯定不够的。

    var size=20*1024*1024;	//20MB
    
    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);
    console.log('能输出吗?');

    那么我们得要想着把没用的给删除掉,提到删除或许第一下想到的是

    delete
    操作,但是这里不推荐使用:

    • 支持性问题,浏览器可能会有不支持情况
    • 在严格模式下有bug

    那么,最简单的方式就是使用了后,赋值为

    null
    undefined

    拓展知识:

    null
    undefined
    的在设计上的区别:

    null其实是一个保留字,而undefined其实是一个变量

    读到这里,可能会有读者不相信,来,我们去控制台跑一下就知道了:

    保留字是不允许赋值的,而undefined你可想成一个比较特殊的变量

    拓展就到这里了,回到上述问题,当每次用完后,我们进行删除操作后,看之前的代码是否还会炸掉:

    var size=20*1024*1024;	//20MB
    
    var arr1=new Array(size);
    arr1=undefined;
    var arr2=new Array(size);
    arr2=undefined;
    var arr3=new Array(size);
    arr3=undefined;
    var arr4=new Array(size);
    arr4=undefined;
    var arr5=new Array(size);
    arr5=undefined;
    var arr6=new Array(size);
    arr6=undefined;
    var arr7=new Array(size);
    arr7=undefined;
    var arr8=new Array(size);
    arr8=undefined;
    var arr9=new Array(size);
    arr9=undefined;
    var arr10=new Array(size);
    arr10=undefined;
    var arr11=new Array(size);
    arr11=undefined;
    var arr12=new Array(size);
    arr12=undefined;
    var arr13=new Array(size);
    arr13=undefined;
    var arr14=new Array(size);
    arr14=undefined;
    var arr15=new Array(size);
    arr15=undefined;
    console.log('能输出吗?');

    执行结果:

    但我们作为程序员工作当然不是一个人的事情,很多情况都是多人合作完成的,为了不给其它的同事造成影响,要养成一个好的习惯,比如我们可以使用

    匿名自执行函数变全局为局部

    举个栗子:

    var size=20*1024*1024;	//20MB
    //匿名自执行函数
    (function(){
    var arr1=new Array(size);
    })()
    防止内存泄漏
    • 滥用缓存
    • 大内存量操作

    缓存是软件工程中非常伟大的发明,所有的优化可以用缓存来解决

    缓存一般都是缓存在全局的。

    继续拓展一下闭包问题:

    我们所谓的闭包, 不是一种具体的写法,而是一种思想! 让变量在内部通过指定的方式给外部访问,而不是直接访问,这就是一个闭包,我们不能纠结于闭包怎么写怎么写。

    手写node服务

    假设写了一个后端访问,每次用户访问,我们将数据记录在a数组里:

    var http=require('http');
    
    var a=[];
    var size=20*1024*1024;	//20MB
    http.createServer(function(){
    function getme(){
    var mem = process.memoryUsage();
    var format = function(bytes){
    return (bytes/1024/1024).toFixed(2)+'MB';
    };
    console.log('Process: heapTotal '+format(mem.heapTotal) +
    ' heapUsed ' + format(mem.heapUsed) +
    ' rss ' + format(mem.rss)
    );
    };a.push(new Array(size));
    getme();
    }).listen(3000); //配置3000端口
    

    访问:http://localhost:3000/

    上述只是我们第一次访问出现的结果,当我们不断地缓存,内存肯定会炸掉,那如何避免缓存滥用情况呢,下文继续探讨:

    后端的同学可能就会想到

    Redis
    ,对于前端而言,有
    localStorage

    通过 缓存加锁 解决滥用问题

    这里主要是对于使用v8引擎来说,原则上尽量不用v8去缓存大的数据,如果使用了v8来缓存的话,我们可通过缓存枷锁来解决这个问题。其实加锁并不是很复杂的问题,如下代码所示,当缓存长度大于4(真实数据根据实际情况而定),我们就将老的缓存清理掉

    var size=20*1024*1024;	//20MB
    var a=[];
    for(var i=0;i<15;i++){
    if(a.length>4){
    a.shift();
    }
    a.push(new Array(size));
    }
    console.log('能输出吗?');

    执行结果:

    显然,我们加锁后,内存就不会炸掉了

    避免大内存量操作

    上文解决了内存泄漏的问题之一,滥用缓存问题,下文我们接着探索大内存量操作给我们内存带来的问题:

    学过node的同学应该知道如下代码:

    对于小文件读取当然不成问题,但如果有4-5个G呢?一次性读取会造成较大的问题

    //node操作文件
    //该api是一次性读取文件到buffer
    fs.readFile();

    因此,我们通常采用管道流来解决上述问题,通过管道送到可写流去(这里就牵扯到了操作系统方面的知识,读者可以继续研究探索)

    createReadStream().pip(write);

    讲完了node方面,在我们前端,其实也有相关问题,比如大文件上传,浏览器100%卡死,解决方法:

    切片上传

    关于切片上传,本文只会简单概况,读者可以更深入研究,主要使用的就是自带的

    slice()
    函数,示例代码如下:

    //js
    //大文件上传问题
    //切片上传
    //file  slice(片)
    
    post(file.slice(0,1000));
    post(file.slice(1000,2000));

    总结

    学如逆水行舟,不进则退
    • 点赞 4
    • 收藏
    • 分享
    • 文章举报
    一百个Chocolate 博客专家 发布了600 篇原创文章 · 获赞 2156 · 访问量 34万+ 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: