【问题标题】:Monitor maximum memory consumption in Node.js process监控 Node.js 进程中的最大内存消耗
【发布时间】:2017-12-03 09:42:44
【问题描述】:

我正在寻找一种跨平台的方法来可靠地监控 Node.js 进程中的最大内存消耗,无论是否存在泄漏。

在我的案例中,流程既是真实的应用程序,也是综合测试。

我希望它能像这样工作

process.on('exit', () => {
  console.log('Max memory consumption: ' + ...);
});

可以使用node --trace_gc ... 以某种方式跟踪内存消耗,但这导致输出难以阅读(并且可能难以以编程方式分析)。此外,即使 RAM 使用量很大,脚本结束过快也不会发生 GC。

根据我在该主题上看到的情况,通常建议使用memwatch,例如:

require('memwatch-next').on('stats', stats => {
  console.log('Max memory consumption: ' + stats.max);
});

但在我的情况下,它仅在 GC 已经发生或根本没有触发时触发,因此它对于确定 RAM 消耗峰值是无用的。

如果可能的话,我宁愿避免使用 node-inspector 这样的 GUI 工具。

能否从应用程序本身或单独的 CLI 跨平台可靠地检索此最大内存消耗作为数字?

【问题讨论】:

  • 我的解决方案是否像您需要的那样跨平台?
  • @EMX 事实上,是的,谢谢,它看起来是我需要的东西。 pidusage 似乎是跨平台的。我试试看。
  • 太好了,希望它能以您需要的方式解决您的问题 :)

标签: javascript node.js profiling


【解决方案1】:

您可以使用 Node.js process.memoryUsage() 方法来获取内存使用情况:

process.memoryUsage() 方法返回一个对象,该对象描述了 Node.js 进程的内存使用情况,以字节为单位。

它返回以下格式的对象:

{
  rss: 4935680,       // Resident Set Size
  heapTotal: 1826816, // Total Size of the Heap
  heapUsed: 650472,   // Heap actually Used
  external: 49879     // memory usage of C++ objects bound to JavaScript objects managed by V8
}

要获得 Node.js 进程中的最大内存消耗,可以使用process.nextTick 方法。 process.nextTick() 方法将回调添加到下一个滴答队列。一旦事件循环的当前轮次运行完成,当前在下一个滴答队列中的所有回调都会被调用。

let _maxMemoryConsumption = 0;
let _dtOfMaxMemoryConsumption;

process.nextTick(() => {
  let memUsage = process.memoryUsage();
  if (memUsage.rss > _maxMemoryConsumption) {
    _maxMemoryConsumption = memUsage.rss;
    _dtOfMaxMemoryConsumption = new Date();
  }
});

process.on('exit', () => {
  console.log(`Max memory consumption: ${_maxMemoryConsumption} at ${_dtOfMaxMemoryConsumption}`);
});

【讨论】:

  • 谢谢,它有效。变量名有错别字。此外,它可能应该在相同的滴答声和退出时运行以获得全面覆盖。
  • @estus 我修复了变量名,感谢您提及这一点。在exit 回调中添加计算怎么样,我认为没有必要(当然如果你不想在那里创建一个巨大的对象:))。该事件通常用于清理内存。
  • 是的,添加计算以退出 除了 nextTick 效果很好。但是我遇到的问题是应该递归设置 process.nextTick 以便在异步脚本中跟踪它,但是间隔不完整的事实阻止它在脚本结束时调用exit。我用setInterval(calculate, 10).unref(); process.on('exit', calculate); process.on('exit', () => {...})解决了它。
  • @alexmac 你知道如何将 npms 添加到用 C&C++ 编写的 nodejs 中吗?
【解决方案2】:

如果您尝试从进程内部对进程进行基准测试,您的内存使用值将会失真。 (如果您想了解更多信息,请发表评论)


这是我编写的一个小(跨平台)工具,用于检查另一个进程的内存使用情况,它生成一个独立进程并每 100 毫秒监视一次内存使用情况,以便找到最高峰,输出每次发现一个新的高峰,一旦孩子结束就停止。

它使用pidusage,这是一个跨平台进程(cpu % 和)内存 PID 的使用

允许自定义 spawn(参数与 spawn 一起传递)[可以为命令行使用而更新]

它也适用于任何节点二进制名称,因为它将重用用于启动此工具的名称。

'use strict'
const UI = {};  var ñ = "    "
const pusage = require('pidusage');
//:Setup the 'cmd' array to be the file and arguments to be used
const ANALYSIS = {cmd:['child.js']}
ANALYSIS.child = require('child_process').spawn(
 process.argv[0], // reuse to work with the same binary name used to run this (node|nodejs|...)
 ANALYSIS.cmd,   // array with filePath & arguments to spawn for this analisis
 { //So the child_process doesn't behave like a child
   detached:true,    
   stdio:['ignore'],
   env:null
 }
);
//:The Analysis
DoAnalysis(ANALYSIS.child.pid);
ANALYSIS.child.unref()
var memPeak = 0;
function PIDStat(){
 pusage.stat(ANALYSIS.pid, function(err, stat) {
  if(err){ CheckError(err) }else{
   if(stat.memory > memPeak){memPeak=stat.memory;PrintStat()}
   setTimeout(PIDStat,100); pusage.unmonitor(process.pid)
  }
 })
}
//:UI (just for display)
function DoAnalysis(PID){
 var s = '═'.repeat(ANALYSIS.cmd[0].toString().length)
 ANALYSIS.pid = PID;
 UI.top = '╒═'+s+'═╕'
 UI.mid = '│ '+ANALYSIS.cmd[0]+' │'
 UI.bot = '╘═'+s+'═╛'
 console.log(UI.x);
 PIDStat()
}
function PrintStat(){
 console.clear()
 console.log('\n',UI.top,'\n',UI.mid,'PEAK MEM. :',memPeak,'\n',UI.bot)
}
function CheckError(e){
 switch(e.code){
  case "ENOENT": console.log("              [the analysis ended]\n"); break;
  default: console.log("[/!\\ error]\n",e); break
 }
}

将产生以下输出:

 ╒══════════╕ 
 │ child.js │ PEAK MEM. : 28737536 
 ╘══════════╛
              [the analysis ended]

此工具可防止您将膨胀添加到您实际想要进行基准测试的进程的代码中,因此您不会获得不同的内存使用值,因为您的膨胀/基准测试代码也会向该进程添加内存使用。

【讨论】:

  • 以上代码在 Node 6 中的 console.clear() 上失败,它最近被添加到 Node API。谢谢,它按预期工作。应该提到的是,它在同步运行的进程上效果不佳,脚本可能会错过它们出现峰值的点。至于膨胀论点,我发现在每个滴答声上运行的 process.memoryUsage() 并在另一个答案中被建议对 CPU 和 RAM 的影响为零,所以这没什么大不了的。
  • @estus 调用如何对 CPU 产生影响? :)
  • @bluehipy 可以忽略不计,而不是字面上的 0。我的立场是正确的,process.memoryUsage() 在每个滴答声中监测时给出 2-3%,在以 10ms 间隔监测时给出 ~0%(这在这样的情况)
猜你喜欢
  • 2013-07-17
  • 2021-12-25
  • 2020-04-21
  • 2011-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-10
相关资源
最近更新 更多