Size: a a a

NodeUA - JavaScript and Node.js in Ukraine

2020 January 29

GS

German Swan in NodeUA - JavaScript and Node.js in Ukraine
Алексей Попов
возможно проще обойти проблему, чем бороться с ней? например, создать пул дочерних процессов, в которых обрабатывать файлы, и периодически эти процессы убивать и пересоздавать
всегда можно костыльнуть, но проблему, которая отображена в примере кода, это не решит
источник

A

Alexander in NodeUA - JavaScript and Node.js in Ukraine
German Swan
всем привет. просьба к опытным разрабам: помогите решить проблему плс.
задача: получить стрим большого файла (пол гига примерно) и обрабатывать его чанками, что бы не хранить весь файл в памяти и не ждать полной загрузки файла.
проблема: написав следующий код я обнаружил, что по мере загрузки файла увеличивается использование памяти, при чем только rss, в то время как heap стабильно очищается. как видно в логах после полной прогрузки файла и выхода из функции main (по идее все ссылки на dataStream должны очиститься garbage collector-ом) rss все еще занимает 64mb. в реальном проекте таких файлов много и желательно обрабатывать их параллельно, но из-за того что использование памяти постоянно растет, приложение просто падает.
вопрос: почему так происходит (может это баг ноды?) и можно ли до закрытия стрима удалять из памяти чанки, которые уже обработаны?
гуглить пробовал. проверять дампы памяти в chrome inspect-е тоже - там только ссылки на какие-то внутренние объекты движка.

Код:
const https = require('https');
const source = 'https://www1.nyc.gov/assets/planning/download/zip/data-maps/open-data/nyc_pluto_19v2_csv.zip';

const consoleMemory = () => {
 const m = process.memoryUsage();
 for (const key of Reflect.ownKeys(m)) {
   m[key] = ~~(m[key] / 1024 / 1024);
 }
 console.log(m);
}

const getDataStream = () => {
 return new Promise((resolve) => {
   https.get(source, resolve);
 });
}

const main = () => new Promise(async (resolve) => {
 const dataStream = await getDataStream();
 let c = 0;
 dataStream.on('data', (data) => {
   c++;
   if (c % 1000) return;
   consoleMemory();
 });
 dataStream.on('end', resolve);
});

(async () => {
 consoleMemory();
 await main();
 await new Promise(res => setTimeout(res, 5000));
 consoleMemory();
})();


Логи:

{rss: 25, heapTotal: 9, heapUsed: 5, external: 0}
{rss: 41, heapTotal: 16, heapUsed: 6, external: 1}
{rss: 51, heapTotal: 16, heapUsed: 7, external: 2}
{rss: 54, heapTotal: 16, heapUsed: 7, external: 2}
{rss: 56, heapTotal: 17, heapUsed: 6, external: 2}
{rss: 57, heapTotal: 17, heapUsed: 6, external: 1}
{rss: 58, heapTotal: 17, heapUsed: 6, external: 0}
{rss: 59, heapTotal: 17, heapUsed: 10, external: 13}
{rss: 60, heapTotal: 17, heapUsed: 9, external: 12}
{rss: 61, heapTotal: 17, heapUsed: 9, external: 11}
{rss: 62, heapTotal: 17, heapUsed: 9, external: 10}
{rss: 63, heapTotal: 17, heapUsed: 9, external: 9}
{rss: 64, heapTotal: 17, heapUsed: 7, external: 2}
Точно не знаю как у вас там потом построена обработка внутри, но если этот же пример написанный на пайпах норм работает. Рсс то увеличивается, то уменьшается.

{ rss: 30, heapTotal: 4, heapUsed: 2, external: 1 }
{ rss: 44, heapTotal: 4, heapUsed: 2, external: 1 }
{ rss: 47, heapTotal: 4, heapUsed: 2, external: 2 }
{ rss: 51, heapTotal: 4, heapUsed: 3, external: 15 }
{ rss: 57, heapTotal: 4, heapUsed: 2, external: 8 }
{ rss: 57, heapTotal: 4, heapUsed: 3, external: 11 }
{ rss: 50, heapTotal: 4, heapUsed: 2, external: 11 }
{ rss: 59, heapTotal: 5, heapUsed: 2, external: 2 }
{ rss: 58, heapTotal: 5, heapUsed: 3, external: 10 }
{ rss: 58, heapTotal: 5, heapUsed: 2, external: 8 }
{ rss: 58, heapTotal: 5, heapUsed: 2, external: 3 }
{ rss: 58, heapTotal: 5, heapUsed: 3, external: 15 }
источник

A

Alexander in NodeUA - JavaScript and Node.js in Ukraine
Получаем файл, пайпим в анзип, потом в парсер цсв - и профит
источник

GS

German Swan in NodeUA - JavaScript and Node.js in Ukraine
Alexander
Точно не знаю как у вас там потом построена обработка внутри, но если этот же пример написанный на пайпах норм работает. Рсс то увеличивается, то уменьшается.

{ rss: 30, heapTotal: 4, heapUsed: 2, external: 1 }
{ rss: 44, heapTotal: 4, heapUsed: 2, external: 1 }
{ rss: 47, heapTotal: 4, heapUsed: 2, external: 2 }
{ rss: 51, heapTotal: 4, heapUsed: 3, external: 15 }
{ rss: 57, heapTotal: 4, heapUsed: 2, external: 8 }
{ rss: 57, heapTotal: 4, heapUsed: 3, external: 11 }
{ rss: 50, heapTotal: 4, heapUsed: 2, external: 11 }
{ rss: 59, heapTotal: 5, heapUsed: 2, external: 2 }
{ rss: 58, heapTotal: 5, heapUsed: 3, external: 10 }
{ rss: 58, heapTotal: 5, heapUsed: 2, external: 8 }
{ rss: 58, heapTotal: 5, heapUsed: 2, external: 3 }
{ rss: 58, heapTotal: 5, heapUsed: 3, external: 15 }
не важно какая там дальше обработка, я ж дал конкретный пример кода. вот как мы видим перед загрузкой файла использовалось 30мб. во время загрузки, да, память ходит туда-сюда. но после загрузки rss увеличен на 30мб в среднем. вопрос в том куда делись эти 30мб?
источник

GS

German Swan in NodeUA - JavaScript and Node.js in Ukraine
Alexander
Получаем файл, пайпим в анзип, потом в парсер цсв - и профит
так и сделал, с этим никаких проблем)
источник

Г

Григорий in NodeUA - JavaScript and Node.js in Ukraine
Всем привет ребята, я обосрался жидким
источник

A

Alexander in NodeUA - JavaScript and Node.js in Ukraine
German Swan
не важно какая там дальше обработка, я ж дал конкретный пример кода. вот как мы видим перед загрузкой файла использовалось 30мб. во время загрузки, да, память ходит туда-сюда. но после загрузки rss увеличен на 30мб в среднем. вопрос в том куда делись эти 30мб?
Дело в том, что рсс может не уменьшаться в 2 случаях. Они используются программой либо их не зареклеймила ОС, потому что ей и так хватает всего.

Поэтому на таком примере понять есть ли там мемори лик или нет.

Нужен файл который раза в 2-3 больше объема оперативной памяти
источник

VD

Vladyslav Dukhin in NodeUA - JavaScript and Node.js in Ukraine
Григорий
Всем привет ребята, я обосрался жидким
Зачем нам эта информация?!
источник

GS

German Swan in NodeUA - JavaScript and Node.js in Ukraine
Alexander
Дело в том, что рсс может не уменьшаться в 2 случаях. Они используются программой либо их не зареклеймила ОС, потому что ей и так хватает всего.

Поэтому на таком примере понять есть ли там мемори лик или нет.

Нужен файл который раза в 2-3 больше объема оперативной памяти
интересно. а можно узнать источник этой инфы? хочу почитать подробнее об этом

проверю с файлом побольше
источник

Г

Григорий in NodeUA - JavaScript and Node.js in Ukraine
Дело в том, что я зашёл в серверную нашей конторы, и не сдержался. В итоге все заискрилось и данные потеряны.
источник

A

Alexander in NodeUA - JavaScript and Node.js in Ukraine
German Swan
интересно. а можно узнать источник этой инфы? хочу почитать подробнее об этом

проверю с файлом побольше
Ну это надо в зависимости от ОС литературу читать о планировщике памяти.
источник

GS

German Swan in NodeUA - JavaScript and Node.js in Ukraine
Alexander
Ну это надо в зависимости от ОС литературу читать о планировщике памяти.
ок, погуглю, спасибо за наводку
источник

A

Alexander in NodeUA - JavaScript and Node.js in Ukraine
В больгинстве случаев для вас важно именно heapUsed. Т.к. он отображает то, как ваш код вляиет на память. Если растёт rss или другое, то скорее всего это штука где-то в ноде, в8 и т.п.
источник

ВС

Владимир Свешников... in NodeUA - JavaScript and Node.js in Ukraine
У меня тут есть не самый умный вопросец по Vue и vue-native-socket
Я его на тостере оформил ибо удобно.
Если кто подключал и есть свободная минутка, буду признателен )
https://qna.habr.com/q/705439
источник

MD

Mikhail Demidoff in NodeUA - JavaScript and Node.js in Ukraine
В вью не разбираюсь, но вот бы все так вопросы оформляли
источник

AS

Andrey Saenko in NodeUA - JavaScript and Node.js in Ukraine
Владимир Свешников
У меня тут есть не самый умный вопросец по Vue и vue-native-socket
Я его на тостере оформил ибо удобно.
Если кто подключал и есть свободная минутка, буду признателен )
https://qna.habr.com/q/705439
тут спросите https://t.me/vuejs_club
источник

ВС

Владимир Свешников... in NodeUA - JavaScript and Node.js in Ukraine
Andrey Saenko
тут спросите https://t.me/vuejs_club
Спасибо, попробуем )
источник

MI

Mykyta Ivanchenko in NodeUA - JavaScript and Node.js in Ukraine
German Swan
не важно какая там дальше обработка, я ж дал конкретный пример кода. вот как мы видим перед загрузкой файла использовалось 30мб. во время загрузки, да, память ходит туда-сюда. но после загрузки rss увеличен на 30мб в среднем. вопрос в том куда делись эти 30мб?
https://nodejs.org/dist/latest-v12.x/docs/api/stream.html#stream_writable_write_chunk_encoding_callback

....will cause poor garbage collector performance and high RSS (which is not typically released back to the system, even after the memory is no longer required)...
Дока говорит что  RSS обычно не возвращается в систему даже если эта память не нужна.
источник

GS

German Swan in NodeUA - JavaScript and Node.js in Ukraine
Mykyta Ivanchenko
https://nodejs.org/dist/latest-v12.x/docs/api/stream.html#stream_writable_write_chunk_encoding_callback

....will cause poor garbage collector performance and high RSS (which is not typically released back to the system, even after the memory is no longer required)...
Дока говорит что  RSS обычно не возвращается в систему даже если эта память не нужна.
понял, спасибо)
источник
2020 January 30

MI

Mykyta Ivanchenko in NodeUA - JavaScript and Node.js in Ukraine
German Swan
понял, спасибо)
Если не получится найти решение лучше - можно попробовать вручную вызывать сборщика мусора:
dataStream.on('data', () => {
   c++;
   global.gc();
   
   if (c % 1000) return;
   
   consoleMemory();
 });

----
node --expose-gc ./stream.js

----
{rss: 19, heapTotal: 4, heapUsed: 2, external: 1 }
{ rss: 27, heapTotal: 5, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 5, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 5, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 5, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 5, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 4, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 5, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 5, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 5, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 4, heapUsed: 2, external: 1 }
{ rss: 28, heapTotal: 5, heapUsed: 2, external: 1 }
источник