Deno.memoryUsage возвращает heapTotal > размер резидентного набора (rss)

В настоящее время у меня возникает проблема, когда rss, возвращаемый из Deno.memoryUsage (для разных приложений), возвращает размер rss, который меньше heapTotal. Это проблема, которая началась в последние несколько дней. Rss начинается выше в некоторых приложениях, а затем в этих приложениях опускается ниже heapTotal. Это одинаково для всех приложений Deno, независимо от любых других используемых фреймворков.

Я использую Deno v. 1.27.2 во всех приложениях. Это проблема для нескольких приложений, компьютеров и разработчиков (я работаю с небольшой командой), а не для какого-либо конкретного технологического стека.

Мое понимание, основанное на каждой статье, которую я прочитал, и из документов Deno, заключается в том, что rss должен представлять общую память процесса в ОЗУ. Он должен включать как heapTotal, так и heapUsed, а также стек вызовов. Насколько я понимаю, он никогда не должен опускаться ниже heapTotal. Это схема, которую я видел везде:

Есть ли у кого-нибудь понимание этого? Я полностью неправильно понимаю размер резидентного набора (rss)? Отличается ли RSS в Deno от Node?

Что мы пробовали:

Я копался в исходном коде ржавчины для Deno и обнаружил, что они используют эту функцию ржавчины для измерения размера резидентного набора:

pub fn total_physical_size(&self) -\> usize {
   unsafe { v8__HeapStatistics__total_physical_size(self) }
}

Который извлекает total_heap_size() из движка v8.

Когда мы собирали статистику памяти после обнаружения ошибки, мы использовали следующую функцию и запускали ее в нескольких приложениях, всегда получая один и тот же результат:

setInterval(() => {
   console.info(Deno.memoryUsage());
}, 500)

Вот выборка значений, которые мы получили от одного из наших приложений.

{ rss: 4423680, heapTotal: 4849664, heapUsed: 3894252, external: 29347 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3906964, external: 33617 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3919676, external: 37887 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3932388, external: 42157 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3945100, external: 46427 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3957812, external: 50697 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3970524, external: 54967 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3983236, external: 59237 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3995948, external: 63507 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 4008660, external: 67777 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 4021372, external: 72047 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 4034084, external: 76317 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 4046796, external: 80587 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 4059508, external: 84857 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 4072220, external: 89127 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 4084932, external: 93397 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 4097644, external: 97667 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 4110356, external: 101937 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4123088, external: 106209 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4135804, external: 110481 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4148520, external: 114753 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4161236, external: 119025 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4173952, external: 123297 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4186668, external: 127569 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4199384, external: 131841 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4212100, external: 136113 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4224816, external: 140385 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4237532, external: 144657 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4250248, external: 148929 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4262964, external: 153201 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4275680, external: 157473 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4288396, external: 161745 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4301112, external: 166017 }
{ rss: 4669440, heapTotal: 4849664, heapUsed: 4313828, external: 170289 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3869548, external: 20807 }
{ rss: 4423680, heapTotal: 4849664, heapUsed: 3882260, external: 25077 }

🤔 А знаете ли вы, что...
JavaScript активно развивается, и новые версии языка регулярно включают новые функции и улучшения.


1
52
1

Ответ:

Решено

Небольшое исследование показывает, что то, что Deno сообщает как «rss», не является резидентным размером процесса; это «выделенный» (в отличие от зарезервированного) размер управляемой кучи. Поэтому оно всегда должно быть больше heapUsed и всегда меньше heapTotal, что соответствует числам, которые вы получаете.

Соответствующая часть ядра Deno/ops_builtin_v8.rs:

fn op_memory_usage(scope: &mut v8::HandleScope) -> MemoryUsage {
  let mut s = v8::HeapStatistics::default();
  scope.get_heap_statistics(&mut s);
  MemoryUsage {
    rss: s.total_physical_size(),
    ...

Таким образом, он даже не пытается измерить то, что вы и я называем «RSS», он просто неправильно переименовывает то, что V8 сообщает как «общий физический размер», который является зафиксированной долей «общего размера кучи».


Для справки: когда я запускаю новый процесс Deno 1.27.2 и спрашиваю его об использовании памяти, я получаю (в байтах, согласно документации):

{ rss: 3682304, heapTotal: 3862528, heapUsed: 3306952, external: 225 }

Оставляем этот процесс и проверяем его фактический RSS с другого терминала:

$ ps -e | grep deno
3713179 pts/4    00:00:02 deno
$ cat /proc/3713179/status | grep VmRSS
VmRSS:    183028 kB

мы видим, что фактический RSS составляет ~ 179 МБ, тогда как «rss» Deno сообщает о 3,5 МБ. Вы можете подать жалобу на Deno, если вам не все равно.


Примечание: выборка размера кучи каждые 500 мс довольно бессмысленна. Вы в любом случае получаете полезные новые данные только после полных циклов GC, и они должны быть с интервалом не менее нескольких секунд даже в сильно загруженных приложениях (возможно, за исключением микробенчмарков, которые стресс-тестируют очень специфические шаблоны распределения). Я бы не проверял размер кучи чаще, чем раз в минуту или около того.