本文是系列文章Node.JS Profile的一部分,完整的文章列表请去总章查看。
本文是Node内存相关文章的其中一篇,主要负责深度解析V8引擎GC相关的技术。
后续V8关于内存回收GC相关的技术演进,会更新在这个段落:
Node的V8引擎支持使用命令行添加option启动的形式,在stdout中输出GC相关内容的日志。GC日志基本上可以说是了解V8 GC行为及状态的唯一方法。
GC日志需要在命令行启动node的时候添加option来启用,相关的option我找到一个不错的gist,里面有描述,这里fork了下:
而本文撰写时参照的Node版本,支持的GC相关option如下:
如果想要在runtime使用代码主动触发GC,则需要在node启动的时候添加option--expose-gc
。
然后在代码里:
if (global.gc) {
global.gc();
}
范例效果:
范例中的图片来自:Are your v8 garbage collection logs speaking to you?Joyee Cheung -Alibaba Cloud(Alibaba Group)
阿里的一个GC日志解析工具,可以在命令行下运行,也可以在代码runtime里运行。该工具的主要作用是将node --trace_gc
输出的内容进行可读化解析,输出成JSON格式,方便后续存储或分析。
当前版本支持的options有:
该工具的最大优势就是内容完全按v8官方输出的来,没有作者主观性的内容增删,仅只是辅助阅读。
缺点是gc的日志输出就在stdout里,如果stdout还有点别的什么正常业务输出,分析起来就麻烦了,先要过滤一次,否则该工具用不了。
第三方的GC信息工具,使用C++编写,作为node的addone运行,只能在代码runtime里运行。输出的内容和官方gc日志的内容不尽相同,相对来说更简洁,适合需要简单功能的用户使用。
优势在于可以在运行时直接获取gc信息(GC时触发监听事件),方便在代码里直接存储到其他地方,事后进行分析。
缺点是输出信息不如官方的gc日志那么详细和多样性。
第三方的GC信息工具,使用C++编写,作为node的addone运行,只能在代码runtime里运行。这个工具关注的不仅仅只是GC,有几个功能有点意思:
更详细的介绍可以看这篇:Node.js 性能调优之内存篇(三)——memwatch-next
优点基本同上面的那个工具,且可以判断leak(其实自己也可以实现)。
缺点是功能相对来说意义不是很大,输出的内容信息不足。
aliyun-node/v8-gc-log-parser只支持事后解析静态日志文件并输出结果,但有的时候我们需要查看下实时的情况。这里有一个脚本可以使用:
该脚本暂时不能分析--trace-gc --trace-gc-verbose
的输出结果,因为该输出是多行的,需要聚合后才可以发给解析工具。有兴趣的可以自己改造下。
在V8官方输出的GC日志当中,--trace_gc
和--trace_gc_verbose
输出的内容都是比较简单的,主要给出了非常实用的:
这些信息基本上能满足大部分的使用情况。
而--trace_gc_nvp
这个option输出的信息则不同,相对于上述的两个option,nvp日志给出的内容相当详细,而且根据GC的类型不同,输出的内容也不尽相同。是用来进行GC细节调优时候非常重要的信息来源。
但可惜的是网上并没有很详细的,针对这个类型输出日志内容的解释,无论是官方的还是社区的。因此最后的手段就是阅读V8的源代码查找里面的信息细节。本文会在当前的章节段落中,将阅读源码获取的解释记录在下面,方便读者查阅。
当然这里会查找源码去寻求解释的也只是一部分比较有价值的内容,并非全部,如果读者有兴趣可以自行查阅源码,下面会给出源码信息。
行文使用的Node V8版本信息:
$ node -e “console.log(process.versions.v8)”
6.0.286.52
因此这里使用的V8分支是:6.0.286
源码主要是V8的:heap.cc和gc-tracer.cc,这两个文件。
gc-tracer.h其实很简单,只是一些显示和输出之类的,主要的逻辑和统计还是在heap.h里。
几个结构和逻辑入口:
pause
:GC暂停时长,单位msgc
:GC类型字符串,这里肯定是s
scavenge_throughput
:
ScavengeSpeedMode
模式的不同,返回的数值可能是:total_size_before
:GC开始时的对象内存大小 bytes(Size of objects in heap set in constructor.),start_object_sizetotal_size_after
:GC结束时的对象内存大小 bytes(Size of objects in heap set in destructor.),end_object_sizeholes_size_before
:
浪费掉的
或处于可用freelist内的
内存空间大小 byteshole
的概念并非一般论的内存空洞,而应该理解为因GC导致的内存空间不连贯
Compacting
的时候会起到判断的作用:Compacting >> 主要发生在某一页中死亡对象留下来的空洞(hole)比较多的时候
holes_size_after
:
allocated
:自上次GC结束到这次GC开始之间的时间里,分配的内存大小 bytes,见赋值promoted
:晋升的对象内存大小 bytes,见定义和gettersemi_space_copied
:新生代内存空间对半拷贝的对象内存大小 bytes,见定义和getterNode
说明很模糊,不清楚是不是内存页的意思,源码可以见Node定义和new_space_nodes_promotion_ratio
:
average_survival_ratio
:新生代对象平均生存率,计算见方法定义promotion_rate
:
semi_space_copy_rate
:
mmc
到reduce_memory
都和上面的意义相同,之后的全部都是按scope分的耗时。
ms
incremental_marking_throughput
:增量标记速率 bytes/ms,代码见方法定义
incremental_walltime_duration
:增量标记总耗时 ms
compaction_speed
:compacting的平均速度,bytes/ms,计算见方法定义reduce_memory
下面的一大段老样子,都是按scope分的耗时。
EOF