Vue 技术栈 教你玩"坏" v8引擎 吃透 js 内存回收机制
写在开头
学习完了ES 6基础,推荐阅读:ECMAScript 6 全套学习目录 整理 完结
现在开始逐步深入Vue 技术栈,想了想,技术栈专栏的主要内容包括:
1、Vue源码分析
2、手把手教 保姆级 撸代码
3、无惧面试,学以致用,继承创新
4、谈谈前端发展与学习心得
5、手写源码技术栈,附上详细注释
6、从源码中学习设计模式,一举两得
7、编程思想的提升及代码质量的提高
8、通过分析源码学习架构,看看优秀的框架
9、项目实战开发
10、面试准备,完善个人简历
暂时想到的就这么多,把这列举的10点做好了,我觉得也OK了,欢迎一起学习,觉得不错的话,可以关注博主,专栏会不断更新,可以关注一下,传送门~
学习目录
为了方便自己查阅与最后整合,还是打算整个目录,关于Vue技术栈前面的几篇优秀的文章:
文章目录
正文
理解内存的意义
- 防止页面占用内存过大,引起客户端卡顿,甚至无响应
- 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
- 收藏
- 分享
- 文章举报
- Vue插件开发一系列api Vue.util.defineReactive 手写实现
- Vue 路由工作流程 hash 与 history
- Vue 技术栈 带你探究 vue-router 源码 手写vue-router
- 2020 零基础到快速开发 Vue全家桶开发电商管理系统(Element-UI)【目录】
- 什么场景下使用vuex
- 新建Vue项目,报错Expected indentation of 2 spaces but found 8
- Vue 设置背景图片样式
- Vue 新手学习笔记:vue-element-admin 给每个页面设置相应的 Title
- 第一个vue项目
- 关于vue-cli创建的项目位置的问题
- 安装vue-cli脚手架
- ngnix 部署 vue项目-基础篇
- Vue项目问题与分析
- Vue项目创建
- 首次加载前端vue项目浅谈
- 浅谈Vue组件的生命周期
- 浅谈vue-router
- Spring Boot+Vue前后端分离,如何避免前端页面 404
- vue一直报般配不匹配
- vue仿淘宝滑动验证码功能(样式模仿)