1. 前言
本文是系列文章Node.JS Profile的一部分,完整的文章列表请去总章查看。
本文是Node内存相关文章的其中一篇,主要负责介绍内存监控相关的API及实践操作。
2. 内存Metrics API
本节介绍几个非常有用的获取内存相关信息的API方法。
- rss:node进程总内存占用量
- heapTotal:总堆内存占用量(已申请下来的)
- heapUsed:实际堆内存使用量
- external:扩展等外部程序的内存占用量
常用来查看基础的内存信息,特别是rss很有用。
- total_heap_size:总堆内存占用量(已申请下来的),同
process.memoryUsage().heapTotal
- total_heap_size_executable:字节码、优化后的代码等可执行的内容占用的内存量
- total_physical_size:找到部分解释说是
Commited size
,测试下来该值
- used_heap_size < total_physical_size < total_heap_size
- Committed memory is, essentially, all the memory which has been allocated by applications, whether it’s used or not.
- total_available_size:剩余可用的堆内存量,包括尚未向操作系统申请的部分,其实就是
heap_size_limit - used_heap_size
- used_heap_size:实际堆内存使用量,同
process.memoryUsage().heapUsed
- heap_size_limit:最大可用堆内存(上限)
- malloced_memory:实际测试是一个很小的值,有解释说是:current amount of memory, obtained via malloc
- peak_malloced_memory:没搜到任何说明,有必要可以读下node源码
- does_zap_garbage:覆盖堆垃圾的模式的开关
常用来查看堆上限大小。
按内存空间分类space
种类不同,给出不同空间的内存使用状况统计。实用性不大,一般来说实际使用中需要关心的其实只有old space,且仅仅只有large object space。
2.4 top & ps
使用系统ps命令更快获取进程的内存占用情况:
ps -p $(pgrep -lfa node | grep leak-and-gc.js | awk '{print $1}') -o rss,vsz
以及:
top -pid $(pgrep -lfa node | grep leak-and-gc.js | awk '{print $1}')
本节会提供实际的内存泄露例子,并指导如何使用工具进行问题点的查找。
3.1 范例
内存泄露的实际例子可以使用下面5.1里的脚本进行试验。
3.2 查找问题
使用node的inspector来进行运行状态分析(当然,这工具可以做更多的事情)。关于Chrome Dev Tool,可以看官方教程。
DEBUG=* node —inspect xxx.js
然后打开浏览器chrome://inspect/
,找到你的脚本进行调试。也可以使用5.3里提到的工具,简化操作。
在打开的分析面板里,选中Memory
tab,一共有3个选项可以操作:
- Take heap snapshot
- 获取node进程的堆快照
- 点击之后需要等一段时间采集数据,然后就可以看到heap数据
- 这个选项信息最全,一般是最常用的内存观察选项
- 一般来说按最右边的
Retained Size
从大到小排序,就找到很有用的信息了
- Record allocation profile
- 以内存使用者的角度查看内存的分配情况
- 在需要知道内存使用大户是哪个部分的业务的情况下很有用
- 一样需要点击之后等一段时间进行采集
- Record allocation timeline
- 以时间轴为单位查看单位时间内的内存分配量
- 在需要知道node的内存与时间关系的情况下很有用
结果页面上会有多个列,里面的意义这里简单介绍下,方便理解和查找问题:
- Constructor:对象构造函数名称
- Distance:对象到根节点的引用层级
- Objects Count:对象的数量
- Shallow Size: 对象本身所占用的内存,这里不包含其引用对象所占的内存
- Retained Size: 对象所占总内存
- Retainers:对象的引用层级关系
和RSS类似,这里的Retained Size
是最重要需要关注的值。
在线上运行时有的时候如果需要看堆快照的话,可以使用第三方库bnoordhuis/node-heapdump在runtime使用代码导出快照。然后使用Chrome Dev Tool打开这个快照文件来查看内容。
Chrome Dev Tool可以加载多个堆快照,并对他们进行比对分析,这对内存量增长变化的分析非常有用。可以在程序里隔一定时间获取一次堆快照,然后线下慢慢分析。
更详细的可以看博客:
4. 核心内存Metrics
本节整理出监控Node内存的时候需要关心的核心Metrics。
4.1 Node内存
- rss:node进程总内存占用量
- heapTotal:总堆内存占用量(已申请下来的)
- heapUsed:实际堆内存使用量
- external:扩展等外部程序的内存占用量,在某些情况下rss很大但堆内存很小的时候,就需要定点关注外部插件使用的内存了
- heapSizeLimit:堆内存上限,建议在node启动的时候每次都确定好堆内存大小
按空间分类的堆内存信息可以酌情收集,如果有需要分析单独的新生代
或老生代
内存情况的话。
4.2 Node GC
所有的GC相关Metrics采集都应该按GC触发的时间节点进行收集,毕竟数据来自GC行为,没有GC行为那也就没数据可采集,所以不可能做到类似CPU和Node进程内存这样的按时间定时进行采集。
- gcTime:GC发生的时间,精度可能需要提高到ms级别,而不是second级别
- gcType:GC类型,一般来说新生代的scavenge回收可以忽略,这个类型GC的量级及可优化性都比较低
- gcPause:GC中断时长,需要按不同GC类型进行分类收集,老生代的
markSweepCompact
数据最为关键
- sizeBefore:GC前内存大小 bytes
- sizeAfter:GC后内存大小 bytes
- holesBefore:GC前内存空洞大小 bytes
- holesAfter:GC后内存空洞大小 bytes
- allocated:GC间,内存分配量 bytes
- promoted:GC间,对象晋升量 bytes
- allocationThroughput:GC间,新生代内存申请速率 bytes/ms
- promotionRatio:当前GC中内存从新生代晋升到老生代的百分比 %
- incrementalWalltime:增量标记时长 ms
- compactionSpeed:内存Compacting速率 bytes/ms
5. 工具
5.1 内存泄露 or 正常运行 范例脚本 {#ID51}
为了观察内存泄露和GC日志,需要一个范例运行的脚本,我这里制作了一个。如何使用请查看该脚本头部的注释:
5.2 GC解析管道脚本 {#ID52}
可配合5.1的脚本一起使用,当然使用你自己的业务脚本也是OK的。
可在node进程使用--inspect
flag时,自动打开chrome的调试tab。
6. 资料
EOF