《深刻浅出node.js》第四章——内存控制(笔记)

内存控制

  1. V8的垃圾回收机制 / 内存限制

    1. V8让JS虚拟机的性能达到了很快的地步,因此node实如今V8上
    2. V8的内存限制:Node中经过JS使用内存只能使用部份内存(64G下大概1.4GB),因此Node没法直接操做大内存对象
    3. V8的对象分配
      1. V8中,全部JS对象都是经过堆进行分配,若是已申请的堆空闲不够分配新的对象,将继续申请堆内存
      2. 经过process.memoryUsage()能够看到如今的内存使用状况
      3. Node在启动的时候能够传递 --max-old-space-size / --max-new-space-size 来调整内存限制的大小,只在初始化有效。
    4. V8 的垃圾回收机制
      1. V8主要的垃圾回收算法:分代式垃圾回收机制
        1. V8中将内存分为新生代和老生代,新生代的对象存活时间短,老生代是存存活活时间长/常驻内存的对象
        2. 新生代中的对象主要经过scavenge算法进行垃圾回收,而在scavenge的具体实现使用了Cheney算法
          1. 将对内存一分为二,每一部分空间成为semi space。
          2. 2个semispace只有一个在使用,一个处于闲置状态。处于使用状态的semi space空间称为from空间,处于闲置状态的空间成为To空间。
          3. 分配对象的时候,先从from空间中开始分配
          4. 垃圾回收时,会检查from空间中的存活对象,存活对象呗复制到To空间,不存活的对象占用的空间将会被释放。
          5. to和from空间 交换
          6. 总之,这个算法的意思就是将存活对象在两个semi space空间中进行复制/翻转
          7. 这个算法牺牲了空间,换取时间;之因此采用这个算法是由于新生代中对象的生命周期很短,占用的内存也少
          8. 当一个对象通过屡次复制和转换依然存活,它会被认为是生命周期较长的对象,从而晋升到老生代中。而晋升的套件通常是2个
            1. 对象是否经历过scavenge回收,若是有,就晋升
            2. To空间的内存占用比是否超过比例,若是超过,直接晋升
        3. 老生代中经常使用Mark - Sweep(标记清除)& Mark - compact(标记整理)方法
          1. mark-sweep 标记清除
            1. 遍历每一个对象,并标记存活的对象,清楚阶段只清楚没有被标记的对象。
            2. 缺点:很容易出现清理以后内存空间不连续,这种内存碎片对后续的内存分配形成问题,由于极可能出现须要分配一个打碎片,全部的碎片空间都没法完成这次分配,就会提早触发垃圾回收
          2. mark-compact 标记整理
            1. 标记每一个对象,将活着的对象往一端移动,移动完成以后,直接清理掉边界外的内存。
          3. incremental Marking 增量标记,减小垃圾回收的停顿时间
            1. 由于每次回收的时候,JS会阻塞,因此把大的垃圾回收工程拆分一小步一小步进行,好比增量标记和增量清理,还有延迟清理(lazy sweeping)
    5. 查看垃圾回收日志
      1. 启动node的时候添加 --trace-gc参数
  2. 高效使用内存

    1. 做用域退出/再也不使用了 就会释放这个做用域里的变量
      1. 变量的主动释放
        1. 若是变量是全局变量,对象将常驻在内存。删除手段有
          1. delete操做删除引用关系
          2. 从新赋值,让旧对象脱离引用关系/ 其实直接设置成空/null不行吗
      2. 闭包(closure)
        1. 闭包致使闭包自身做用域不获得释放
  3. 内存指标

    1. 查看内存使用状况:经过process.memoryUsage()能够看到如今的内存使用状况
    2. 查看进程的内存使用,process.memoryUsage()
    3. 查看系统的内存占用,os模块的totalmem() 和 freemem() 用来查看系统的总内存和限制内存
    4. 堆外内存:咱们把不是经过V8分配的内存叫作 堆外内存
  4. 内存泄漏

    1. 形成内存泄漏的缘由常见的有
      1. 缓存
      2. 队列消费不及时
      3. 做用域未释放
    2. 内存 != 缓存,慎重
      1. 一旦命中缓存,就能够节省一次I/O的时间;可是一个对象被看成缓存使用,意味着它常驻在老生代中;缓存中存储的键越多,长期存活的对象也就越多,那么垃圾回收旧会作无用功
      2. 缓存限制策咯:限制缓存的无限增加
        1. 做者写过一个limitablemap模块——P128
      3. 缓存的解决方案
        1. 直接将内存做为缓存的方案,除了缓存大小的顾虑外,还要考虑进程之间没法共享内存,进程内使用缓存将致使缓存不可避免的有重复,浪费物理空间
        2. 解决方案:采用进程外的缓存,进程自身不存储状态
          1. 将缓存转移到外部,减小常驻内存的对象的数量,让垃圾回收变得高效
          2. 进程之间能够共享缓存
        3. 常见的缓存方案( 有客户端)
          1. Redis
          2. Memcached
    3. 关注队列状态,由于也有可能形成内存泄漏
  5. 内存泄漏排查

    1. 常见工具:v8-profiler(3年没维护了),node-heapdump,node-mtrace,dtrace,node-memwatch
  6. 大内存应用

    1. 使用stream模块处理大文件(因为V8内存限制,咱们没法经过fs.readFile()和fs.writeFile()直接对大文件进行操做)javascript

    2. 使用fs.createReadStream() / fs.createWriteStream()方法经过流的方式实现对大文件的操做java

      1. var reader = fs.createReadStream('in.txt');
        var writer = fs.createWriteStream('out.txt');
        reader.on('data',function(chunk){
            writer.write(chunk);
        })
        reader.on('end',function(){
            writer.end;
        })
        
        //利用es6 中的pipe,简写后
        
        var reader = fs.createReadStream('in.txt');
        var writer = fs.createWriteStream('out.txt');
        reader.pipe(writer);
        复制代码
相关文章
相关标签/搜索