Node使用的是Chrome的V8引擎执行javascript,由于运行在浏览器和服务端的场景不同,浏览器中网页的运行时间较短,随着网页的关闭,相应的内存就会被回收,所以对精细的内存的管理需求度不高,但是在服务器端由于程序处于长时间运行中,为了达到高性能的服务端程序,就需要良好的垃圾回收策略和管理策略(机器和我们本身的控制) 良好的管理策略能更好的使垃圾回收运行,垃圾回收是影响性能的因素之一
由于V8在内存回收算法的使用的考虑下,V8给系统分配使用的内存是存在一定的限制的
在垃圾回收机制上,V8主要根据内存中对象在内存中的特征,对内存中的对象进行分区的管理.以下为V8的堆内存的划分
在新生区的内存空间中,由于新生区内存对象的特征是生存周期短,这部分采用Cheney算法.Cheney算法是一种采用复制方式实现的垃圾回收算法,它将新生代内存空分为两个区域:
处于使用状态的From区域和处于闲置状态的To区域.在分配对象的时候,会现在From区域中进行对象的分配,在进行垃圾回收的时候,将From区域中存活的对象copy到To区域并且将From区域和To区域进行角色互换.
当一个对象在进行多次复制仍然存活,它就会被认为是存活周期较长的对象,它就会被转移到老生区域中.对象从新生区域移动到老生区域的过程称为晋升. 晋升主要有两个条件:
- 一个对象时候经历过Scavenge回收(也就是新生区域的垃圾回收)
- To空间的内存占比(当To区域已经使用了25%的时候,从From区域复制的对象会直接晋升到老生区域.因为To区域会在接下来转换为From区域供新的内存的分配,需要预留合适大小的空间)
在老生区域主要采用Mark-sweep 和Mark-Compact 和 Incremental Marking等多种方式结合的方式来达到对老生区进行整理的目的
- Mark-sweep 标记清除 主要分为标记和清除 在标记阶段,垃圾回收器会标记活着的对象,在清除阶段会清除没有被标记的对象
- Mark-Compact 标记整理 它是在标记清除的基础上的,在进行标记的过程中,将活着的对象往一侧进行移动,这样就消除了内存的间隙,方便后续内存空间的分配
- Incremental Marking 增量标记 在进行标记的时候,需要将应用程序的逻辑进行暂停,所以产生了增量标记,每次只标志部分,在让位出时间来执行应用程序.
变量是引用的情况下需要主动的释放内存,例如对引用进行重新的赋值,这样在垃圾回收器运行的时候,就会主要回收内存.
闭包是外部作用域访问内部作用域中变量的方法,在使用完闭包后,要注意中间函数的释放
Buffer对象的内存分配不经过V8的内存分配机制,所以在处理文件I/O的时候可以使用Buffer来达到对堆外内存的使用
造成内存泄漏的原因是我们的操作使得本应在垃圾回收中被回收的内存无法被回收,变成老生区对象.主要有以下几个原因
- 缓存
- 队列消费不及时
- 作用域未释放
当我们将一个对象常驻内存中作为缓存使用的时候(老生区),垃圾回收算法每次都会对它进行操作,这就导致垃圾回收在做无用功并且我们对这个缓存对象的大小不做控制的时候,还可能会导致它占用过大的内存空间,导致进程的退出,并且内存作为缓存这种方案无法再各个进程间进行共享,解决方案是使用缓存软件,他们拥有良好的管理策略,能更好的提升应用的性能.
在使用队列(数组)来完成特殊的任务请求的时候,当任务的消耗低于生产的时候,就会造成堆积,产生内存泄漏.解决方案是设置警告或者超时响应等
对于大内存应用,例如读取大的文件,可以使用node中的stream模块并且它继承了event模块.