美文网首页web前端
node内存泄露问题

node内存泄露问题

作者: 姜治宇 | 来源:发表于2020-04-12 17:35 被阅读0次

node对内存泄露十分敏感,这个不同于浏览器,一旦内存堆积,垃圾回收无法释放,会耗费大量时间进行对象扫描,应用会变慢,直到进程崩溃。

v8垃圾回收机制

v8的垃圾回收策略叫分代式垃圾回收机制。怎么说呢?
这个分代,就是把内存分成两部分:新生代(new space)和老生代(old space)。
新生代中的对象为存活时间较短的对象,老生代中的对象为存活时间较长常驻内存的对象。

新生代的垃圾回收——Scavenge

Scavenge就是拾荒的意思,它将堆内存一分为二,每一部分称为半空间semispace。
在这两个semispace空间中,只有一个处于使用中,另外一个处于闲置状态。处于使用状态的semispace称为From空间,处于闲置状态的semispace称为To空间。
当我们分配对象时,先是从From空间中分配。当From空间一旦满了就会进行垃圾回收,这时会检查From空间中存活的对象,把这些存活的对象复制到To空间中,而垃圾对象占用的空间会被释放掉。
完成复制后,From空间和To空间的角色会发生互换,To变From,From变To,然后重复前面的过程。
这种算法比较简单,适合比较活跃的对象,但如果某对象经历过一次Scavenge回收,下一次又继续使用,那说明这个对象存活时间较长,会将该对象复制到老生代空间里去。

老生代的垃圾回收——Mark-Sweep和Mark-Compact

Mark-Sweep就是标记清除,步骤是:
垃圾回收时,先遍历堆内存的所有对象,标记存活的对象,然后清除掉未标记的对象。
但清除完成后,内存会有很多碎片,我们需要整理一下,有点像磁盘工具,这就是Mark-Compact。
既然内存泄露问题如此重要,那如何防范呢?

监听gc垃圾回收情况

我们可以使用memwatch这个工具来监听内存的垃圾回收情况。

npm install memwatch-next -S

下面做一个测试。
在arr这个数组中无限注入回调函数,导致arr这个变量始终无法释放,这个对象会常驻于老生代空间中,运行几次后必然会导致溢出现象。

let http = require('http')
let memwatch = require('memwatch-next')
let count = 1
let arr = []
memwatch.gc()//测试:手动调用垃圾回收
memwatch.on('leak',(info)=>{
    console.log(info) //5次以上
})
memwatch.on('stats',(stats)=>{
//每执行一次 gc,触发该事件,并打印内存相关信息。
    console.log(count++,'=>',stats)
})

let server = http.createServer((req,res)=>{
//内存泄露事件,触发该事件的条件是:连续5次gc垃圾回收后,内存还是增长的
    for(let i=0;i<100000;i++){
       arr.push(function(){})
    }
    res.end('ok')
})
server.listen(3009)

stats事件的打印结果:

{ num_full_gc: 6,//完整的垃圾回收次数
  num_inc_gc: 16,//增长的垃圾回收次数
  heap_compactions: 6,//内存压缩次数
  usage_trend: 0,//使用趋势
  estimated_base: 54914256,//预期基数
  current_base: 54914256,//当前基数
  min: 12826192,
  max: 54914256 }

leak事件的打印结果:

{ growth: 67250808,
  reason: 'heap growth over 5 consecutive GCs (10s) - -2147483648 bytes/hr' 
}

我们可以利用log4j或其他日志工具,将这些堆栈信息输出到日志里以便分析。

堆内存快照

通过memwatch可以知晓内存是否有泄露,那如何定位到具体问题呢?
我们可以用另外一个工具——heapdump。

npm install heapdump -S

这个工具可以抓取堆快照,并保存为json的格式,我们可以将快照文件用chrome浏览器的devtools打开,选择comparison比较视图来定位问题。

let http = require('http')
let memwatch = require('memwatch-next')
let heapdump = require('heapdump')
let count = 1

let arr = []
dump()//无任何操作,拍第一个堆快照,作为参照系
function dump() {
    //文件名
    const filename = `${__dirname}/heapdump-${process.pid}-${Date.now()}.heapsnapshot`;

    heapdump.writeSnapshot(filename, () => {//保存快照
        console.log(`${filename} dump completed.`);
    });
}

memwatch.on('leak',(info)=>{
    console.log(info)
    dump()//第二个快照,对比第一个快照
})
memwatch.on('stats',(stats)=>{
    console.log(count++,'=>',stats)

})




let server = http.createServer((req,res)=>{

    for(let i=0;i<100000;i++){
       arr.push(function(){})
    }
    res.end('ok')
})
server.listen(3009)

得到快照信息:

heapdump-59352-1586653612856.heapsnapshot
heapdump-59352-1586653617974.heapsnapshot
打开控制台,选择Memory,右键profiles,选择load,即可加载本地快照文件: compare.jpg

相关文章

  • node内存泄露问题

    node对内存泄露十分敏感,这个不同于浏览器,一旦内存堆积,垃圾回收无法释放,会耗费大量时间进行对象扫描,应用会变...

  • Android 内存优化总结&实践

    内存泄露 大部分的内存问题都是内存泄露导致的,Android里也有一些很常见的内存泄露问题这里简单罗列下: 详细见...

  • Java弱引用学习 WeakHashMap、ReferenceQ

    上一篇文章 Java内存泄露学习 ThreadLocal真的会内存泄露吗 提到ThreadLocal内存泄露的问题...

  • 关于华为inputMethodManager内存泄露

    一个关于内存泄露的问题,相信使用华为手机检测内存泄露的时候都会发现inputMethodManager内存泄露这个...

  • 内存泄露总结

    内存泄露会产生的问题: 1:内存泄露造成的第一个问题是异常,包括内存分配失败,OOM。 2:内存泄露造成的第二个问...

  • 内存泄露问题

    在使用系统的CF或者CA开头的类,一定要注意,自己管理内存,要不容易内存泄露 值得一提的是,今天遇到了一个比较特别...

  • 内存泄露系列文章(一) - 内存泄露原因及影响

    前言 内存泄露系列文章内存泄露系列文章(一) - 内存泄露原因及影响内存泄露系列文章(二) - 内存泄露监测及分析...

  • 内存泄露系列文章(三) - 内存泄露解决方案

    前言 内存泄露系列文章内存泄露系列文章(一) - 内存泄露原因及影响内存泄露系列文章(二) - 内存泄露监测及分析...

  • Android内存泄露详解

    内存泄露 在开发应用的过程中,我们总会遇到内存泄露的问题。现在通过代码列出一些常见的内存泄露的情况以及解决方案。 ...

  • Android 性能优化(内存泄露)

    内存泄露可以引发很多的问题,常见的内存泄露导致问题如下: 应用卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发...

网友评论

    本文标题:node内存泄露问题

    本文链接:https://www.haomeiwen.com/subject/uinpmhtx.html