V8内存机制
- 常规而言,64位系统可用内存为1.4G,32位为0.7G,以 1.5 GB 的垃圾回收堆内存为例,最快也要 50毫秒,全量GC 需要 1 秒以上
内存组成
- 分代式垃圾回收机制
内存分为新生代的内存空间和老生带的内存空间
新生代内存空间
- 设置指令: node --max-new-space-size // 默认1024 KB
- 64位系统为32MB 32位系统16M
Scavenga 算法
- 将内存分为 from 和 to , 先将对象分配在 from 中,在进行垃圾回收的时候,将存活的对象,从 form 复制到 to 中(如果to空间占用已经超过 25% ,则直接进入老生带内存空间),复制完之后,释放 from 。同时 to 空间作为下一个 from 空间。每一个已经从 from->to 空间的对象,再下一次GC之后,如果已经经历过 Scavenga 算法,将进入老生带内存空间而非 to 空间
老生代内存空间
- 设置指令: node --max-old-space-size // 默认1700 MB
- 经历过 Scavenga 算法的对象,通常是活对象居多,每次只需要清除少量的死对象。
Mark-Sweep & Mark-Compact 算法
Mark-Sweep 标记清除
- 进行一次标记清楚回收之后,内存空间会出现不连续的状态,这种内存碎片会对后续的内存分配造成问题,因为很可能出现需要分配一个大对象的情况,这时所有的碎片空间都无法完成此次分配,就触发没必要垃圾回收。因此,Mark-Compact 算法被提出来。
Mark-Compact 标记整理
在空间不足以对新生代晋升过来的对象进行分配时,才使用 Mark-Compact
- 对象再标记死亡之后,在整理的过程中,将活着的对象往一端移动,移动完成后,直接清理掉边界外的内存
Incremental Marking 增量标记
为了降低全堆垃圾回收带来的停顿,V8 先从标记阶段入手,将原本要一口气停顿完成的动作改为增量标记,也就是拆分为许多小“步进”,没做完一“进步”,就让 js 应用逻辑执行一小会儿,垃圾回收和应用逻辑交替执行直到标记阶段完成。
lazy-sweeping 延迟清理
increamental-compaction 增量式整理
并行标记
并行清理
Buffer
- Buffer对象的内存分配不是在V8的堆内存中,而是在Node的C++层面实现内存的申请。
- 因为在处理大量的字节数据,不能采用需要一点内存就向操作系统申请一点内存的方式,这可能造成大量的内存申请的系统调用,对操作系统有一定压力。
slab 分配机制
createReadStream 使用用例
var fs=require('fs')
var rs =fs.createReadStream('GC.md') // 注释1 {highWaterMark:11}
// 注释2 rs.setEncoding('utf8')
var d =''
rs.on('data',(e)=>{
d+=e
})
rs.on('end',()=>{
console.log(d)
})
复制代码
d+=e => d=d.toString()+e.toString()
如果打开注释的内容,将会出现 ❓
- 原因是,文件读取时一部分一部分读,如果中途折断了,原先属于那个字符的字节,将会被划分到下一个流中读取,原先的字节就丢失了,造成解码失败。
- 处理截断的话,本质是用的 decoder 对象,将之前截断的部分存下来,与下一个流进行拼接。目前只能完成utf-8,base64,ucs-2/utf-16lf 三者中编码。
转载于:https://juejin.im/post/5d06eebef265da1b8b2b5de1