Подтвердить что ты не робот

Контролируйте максимальное потребление памяти в процессе Node.js

Я ищу кросс-платформенный способ надежного мониторинга максимального потребления памяти в процессе Node.js, независимо от того, существуют ли утечки или нет.

Процессы в моем случае - это как реальные приложения, так и синтетические тесты.

Я ожидаю, что он будет работать как

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

Можно было как-то отслеживать потребление памяти с помощью node --trace_gc ..., но это привело к выводу, который трудно читать (и, вероятно, сложно анализировать программно). Кроме того, GC не произошел, когда script закончился слишком быстро, даже если использование ОЗУ было существенным.

Из того, что я видел по этому вопросу, обычно предлагается memwatch для этого, например:

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

Но в моем случае это срабатывало только тогда, когда GC уже произошел или вообще не запускался, поэтому было бесполезно определять пики потребления памяти.

Я бы предпочел избегать инструментов GUI, таких как node-inspector, если это возможно.

Может ли это максимальное потребление памяти быть надежно извлечено в виде числа из самого приложения или одного CLI, кросс-платформенного?

4b9b3361

Ответ 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}');
});

Ответ 2

Если вы попытаетесь сравнить процесс с самим собой, у вас будут искаженные значения использования памяти. (если вы хотите получить дополнительную информацию об этом, просто прокомментируйте)


Это небольшой (кроссплатформенный) инструмент, который я закодировал для проверки использования памяти другим процессом, он запускает независимый процесс и отслеживает каждые 100 мс для использования памяти, чтобы найти самый высокий пик, выходы каждый раз, когда новый пик обнаруживается и останавливается после того, как ребенок закончил.

Он использует pidusage, который является кроссплатформенным процессом (cpu% и) память использованием PID

Позволяет настраивать икру (аргументы должны быть переданы вместе с икру) [может быть обновлено для использования в командной строке]

Он также будет работать с любым двоичным именем node, так как он будет повторно использовать тот, который используется для запуска этого инструмента.

'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]

Этот инструмент не позволяет вам добавить раздувание в код процесса, который вы на самом деле хотите сравнить, так что вы не получите разные значения использования памяти, так как ваш код bloat/benchmarking также добавит использование памяти в этот процесс.