《深入浅出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()直接对大文件进行操作)

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

      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);
        复制代码

转载于:https://juejin.im/post/5ba8e2cc5188255c9d561819

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值