什么是V8引擎?
V8使用C++开发,并在谷歌浏览器中使用。在运行JavaScript之前,相比其它的JavaScript的引擎转换成字节码或解释执行,V8将其编译成原生机器码(IA-32, x86-64, ARM, or MIPS CPUs),并且使用了如内联缓存(inline caching)等方法来提高性能。有了这些功能,JavaScript程序在V8引擎下的运行速度媲美二进制程序。
V8内存限制
Chrome V8引擎限制了所能使用的内存极限(64位为1.4GB,32位为1.0GB)
当我们在代码中声明变量并赋值时,所使用对象的内存就分配在堆中.如果已申请的堆空间内存不够分配新的对象,将继续申请堆空间,直到堆的大小超过V8的限制为止.当超过V8限制时,项目就会直接崩塌。
v8的垃圾回收机制
v8 使用的是GC算法,采用了分代式垃圾回收机制。V8 将内存(堆)分为新生代和老生代两部分。
新生代算法
新生代中的对象一般存活时间较短,使用 Scavenge GC 算法。
大多数的对象被分配在这里,这个区域很小但是垃圾回特别频繁。在新生代分配内存非常容易,我们只需要保存一个指向内存区的指针,不断根据新对象的大小进行递增即可。当该指针到达了新生代内存区的末尾,就会有一次清理(仅仅是清理新生代)
在新生代空间中,内存空间分为两部分,分别为 From 空间和 To 空间。在这两个空间中,必定有一个空间是使用的,另一个空间是空闲的。新分配的对象会被放入 From 空间中,当 From 空间被占满时,新生代 GC 就会启动了。算法会检查 From 空间中存活的对象并复制到 To 空间中,如果有失活的对象就会销毁。当复制完成后将 From 空间和 To 空间互换,这样 GC 就结束了
老生代算法
老生代所保存的对象大多数是生存周期很长的甚至是常驻内存的对象,而且老生代占用的内存较多。
老生代中的垃圾回收策略是Mark-Sweep(标记清除)和Mark-Compact(标记整理)相结合。
标记清除
标记清除分为标记和清除两个阶段。在标记阶段需要遍历堆中的所有对象,并标记那些活着的对象,然后进入清除阶段。在清除阶段总,只清除没有被标记的对象。由于标记清除只清除死亡对象,而死亡对象在老生代中占用的比例很小,所以效率较高
标记清除有一个问题就是进行一次标记清楚后,内存空间往往是不连续的,会出现很多的内存碎片。如果后续需要分配一个需要内存空间较多的对象时,如果所有的内存碎片都不够用,将会使得V8无法完成这次分配,提前触发垃圾回收。
标记整理
标记整理正是为了解决标记清除所带来的内存碎片的问题。标记整理在标记清除的基础进行修改,将其的清除阶段变为紧缩极端。在整理的过程中,将活着的 对象向内存区的一段移动,移动完成后直接清理掉边界外的内存。紧缩过程涉及对象的移动,所以效率并不是太好,但是能保证不会生成内存碎片。
优化内存的技巧
- 尽量不要使用全局变量
因为我们都知道,全局变量是一直存在内存中,不会被销毁的。知道程序运行结束才会被销毁。假如全局变量占用内存太大的话,会对整个项目造成严重的后果 - 全局变量使用后记得销毁
- 用匿名自执行函数变全局为局部
(function(){
})()
- 尽量避免闭包(这种说法其实是错的)
闭包只有在IE低版本浏览器下才会造成内存泄漏的问题;
测试内存占用的方式
node中使用process.memoryUsage()
function getme() {let mem = process.memoryUsage();let format = function (bytes) {return (bytes/1024/1024).toFixed(2)+"MB";}console.log('heapTotal:'+format(mem.heapTotal)+'heapUsed:'+format(mem.heapUsed))}
浏览器中:window.performance