Node js сколько оперативной памяти
Всем доброго времени суток.
Столкнулся со следующей ситуацией и по-моему это не нормально:
У меня на сервере стоит MySQL база данных, на ней висят 2 сайта с маленькой посещалкой (500-700 человек в сутки), так вот эта база съедает 500-600 мб памяти сервера и как мне объяснили, это слишком много и мне необходимо базу оптимизировать. Может быть, база большая, обрабатывает запросы и возможно обращения к ней не особо оптимизированы, не знаю, но это может быть.
Но вот я поднял Nodejs сервер с самым стандартным api c CRUD на борту. Посмотрел сколько съедается памяти и обомлел, больше 1000 мб занимает Nodejs сервер, которые в данный момент даже запросы не обрабатывает, он просто запущен для личного использования. Это вообще нормально? Нод сервер так и должен есть память? Или у меня скорее всего где-то утечка памяти в конфигурации нод сервера?
- Вопрос задан более трёх лет назад
- 2153 просмотра
По умолчанию процесс Node.js расчитывает, что ему доступны аж 1.5 Gb памяти.
Параметр --max_old_space_size задаёт максимальный размер т.н. Old Space – части Heap, куда попадают неактивные и не нужные данные. Это, на практике, область, занимающая наибольшую память. Именно её есть смысл ограничить, чтобы процесс Node занимал поменьше памяти.
Но у меньшей памяти есть и своя цена: чаще будет запускаться «сбор мусора» (garbage colleciton) – процесс очистки из памяти неиспользуемых данных, к которым более нет ссылок из глобальных или активных локальных переменных. Это блокирующий процесс – Node полностью останавливается на время очистки памяти, не реагирует на запросы и не выполняет никаких действий.
Итого, чтобы занимать меньше памяти, нужно запускать Node с параметром:
node --max_old_space_size=128 index.js
Тут указано занимать под old data не более 128 Mb памяти, т.е. весь Node займёт незначительно больше.
Можно задать переменную окружения WEB_MEMORY которой установить доступный процессу node.js объем памяти в Mb: WEB_MEMORY=128 bash startup.sh , если использовать для запуска node приложения скрипт, предложенный автором статьи (на англ.), откуда я всё это только что сам узнал )
Ок, спасибо. Тогда еще вопрос, а "можно", это значит нужно? То есть если я ограничу объем памяти до 256 мб для веба, это не сделает работу сервера очень тормознутой и не юзабильной? Или с объемом памяти надо играться в зависимости от нагрузки на сервер? Типо пока я сам с ним играюсь, то можно 128 мб, а когда пойдет народ, тогда надо будет поднимать до 1гб.
Но в итоге, то что у меня он жрет 1гб, это нормально? Он просто разложился на всю доступную память как я понял.
У меня на сервере стоит MySQL база данных, на ней висят 2 сайта с маленькой посещалкой (500-700 человек в сутки), так вот эта база съедает 500-600 мб памяти сервера и как мне объяснили, это слишком много и мне необходимо базу оптимизировать.
Но вот я поднял Nodejs сервер с самым стандартным api c CRUD на борту. Посмотрел сколько съедается памяти и обомлел, больше 1000 мб занимает Nodejs сервер, которые в данный момент даже запросы не обрабатывает, он просто запущен для личного использования.
Ищите утечки. Когда сервер запускается, сколько он весит? Каждый запрос увеличивает аппетиты? - Вы что-то сделали не правильно!
Нет, на DLE, плагины есть, но он стартуют по моей указке, а не крутятся в бг
Да вот не понятно что можно было сделать не правильно, так как это самый простой CRUD, сделанный по уроку на хабре
Вот 1гб и весит при старте
Позже тестану и посмотрю. Как понимаю, если будет еще больше есть при запросах, то точно утечка памяти в настройке сервера?
Если вы ломаете голову над тем, почему ваше приложение JavaScript преподносит неприятные сюрпризы в виде сильного торможения, низкой производительности, продолжительных задержек или частых сбоев, и все ваши старательные попытки выяснить причину ни к чему не приводят, то, скорее всего, в вашем коде происходят утечки памяти.
Это довольно распространенная проблема. Дело в том, что многие разработчики пренебрегают управлением памятью из-за неправильных представлений об ее автоматическом выделении и освобождении в современных высокоуровневых языках программирования, например JavaScript.
Своевременно же не решенный вопрос ее утечек может обернуться резким снижением производительности приложения вплоть до невозможности его нормального использования.
Пространство Интернета постоянно пополняется сложными жаргонизмами, в которых весьма непросто разобраться. Данная статья будет построена по другому принципу — просто и понятно. Вы узнаете о том, что такое утечки памяти и каковы их причины; как их легко обнаружить и диагностировать с помощью Chrome Developer Tools.
Начнем с того, что ключ к их пониманию лежит в понимании принципов управления памятью в Node.js. А это, в свою очередь, означает, что мы должны разобраться, как это управление осуществляется движком V8, используемым Node.js для JavaScript.
Вкратце напомню вам структуру памяти в V8.
Главным образом она делится на две области: стек (stack) и кучу (heap).
1.Стек — это область памяти, в которой хранятся статические данные, включающие фреймы методов/функций, примитивные значения и указатели на объекты. Он управляется ОС.
2.Куча — это самая большая область памяти, в которой V8 хранит объекты или динамические данные. Здесь же происходит сборка мусора.
Цитируя Дип К Сасидхаран, разработчика и одного из авторов книги “Развитие полного стека с JHipster”, отметим, что
“V8 управляет памятью кучи с помощью сборки мусора. Проще говоря, он освобождает память, используемую висячими объектами, т.е. объектами, на которые нет прямых или косвенных (через ссылку в другом объекте) ссылок из стека, для освобождения пространства с целью создания нового объекта.
Сборщик мусора в V8 отвечает за восстановление неиспользуемой памяти для повторного ее применения в процессе работы движка. Сборка мусора происходит по поколениям (объекты в куче распределяются по группам в зависимости от времени жизни и удаляются на разных этапах). В V8 существуют два этапа и три разных алгоритма сборки мусора”.
Простыми словами, утечка памяти — это не что иное, как фрагмент памяти в куче, который больше не нужен приложению, но который не был возвращен оперативной системе сборщиком мусора.
И вот мы имеем неиспользуемый фрагмент памяти. Со временем результатом накопления таких фрагментов станет ваше приложение, сигнализирующее о нехватке памяти для работы или даже ОС, требующая места для выделения памяти. А все это вместе чревато торможениями и/или выходом приложения или даже ОС из строя.
Автоматическое управление памятью, подразумевающее сборку мусора в V8, предназначено для предотвращения ее утечек. Например, циклические ссылки больше не вызывают беспокойства, но все-таки могут возникать из-за нежелательных ссылок в куче или по каким-либо другим причинам.
Рассмотрим несколько самых распространенных причин:
1.Глобальные переменные. Поскольку на них в JavaScript ссылается корневой узел (глобальный объект window или ключевое слово this ), то они никогда не подвергаются сборке мусора в течение всего жизненного цикла приложения и будут занимать память до тех пор, пока оно выполняется. И это относится ко всем объектам, на которые ссылаются глобальные переменные, а также к их потомкам. Наличие большого графа объектов со ссылками из корня может привести к утечке памяти.
2. Множественные ссылки. Ситуации, когда на один и тот же объект ссылаются несколько объектов, также могут вызвать утечку памяти при условии, что одна из ссылок становится висячей.
3. Замыкания. Замыкания JavaScript обладают превосходным свойством запоминать окружающий их контекст, вследствие чего ссылки на крупные объекты кучи, используемые в них, сохраняются дольше, чем требуется.
4. Таймеры и события. Использование setTimeout , setInterval , Observers и слушателей событий может вызвать утечки памяти в том случае, если ссылки на объекты кучи хранятся в их обратных вызовах без правильной обработки.
Теперь, когда мы разобрались в причинах возникновения утечек памяти, давайте посмотрим, как их избежать и какие практики взять на вооружение для эффективного использования памяти.
Обработка данных по частям
Иногда нужно обработать большой набор данных. Например, вы пишите программу, которая принимает данные из CSV файла, очищает их и добавляет в БД (это называется ETL: извлечение, трансформация, загрузка).
Если в такой ситуации программе начинает не хватать памяти, попробуйте разделить данные на несколько частей.
Подробнее о том, как сделать это в MongoDB в этом ответе на StackOverflow.
Изменение ограничения памяти для всей среды Node.js
Чтобы изменить лимит памяти для всей среды, нужно установить значение переменной NODE_OPTIONS в конфигурационном файле (его расширение .bashrc, bash_profile или .zshrc и т. п.).
Умеренное использование глобальной области видимости
В целом будет лучше, если вы воздержитесь от использования глобальной области видимости и глобальных переменных насколько это возможно.
- Минимизируйте использование глобальной области видимости. Вместо этого рассмотрите возможность применения локальной области внутри функций, так как они будут удалены в процессе сборки мусора для освобождения памяти. Если вам вынужденно приходится прибегать к использованию глобальной переменной, то задайте ей значение null , когда в ней уже не будет необходимости.
- Используйте глобальные переменные только для констант, кэша и переиспользуемых объектов-одиночек. Не стоит применять их в целях избежания передачи значений в коде. Для обмена данными между функциями и классами передавайте значения в качестве параметров или атрибутов объектов.
- Не храните крупные объекты в глобальной области видимости. Если же вам приходится это делать, то не забудьте определить их как null, когда они более не нужны. В отношении объектов кэша рекомендуется установить обработчик для периодической очистки, препятствующий их неограниченному росту.
Как избежать недостатка памяти в Node.js
Вот три альтернативных решения, которые позволят уменьшить потребление памяти.
Плюсы:
- скорость работы,
- распространенность (в ВУЗАХ многих стран изучают java, также на java удобно изучать ООП),
- огромный набор библиотек.
Что означает эта ошибка?
По умолчанию в Node.js установлен лимит памяти, который не позволяет программе занять слишком много памяти и уронить всю систему. Лимит отличается на разных версиях Node.js и архитектурах (32бита или 64бита).
How to Keep Track of Node.js Memory Leaks
The process.memoryUsage function via the heapUsed field is somewhat useful. One way to debug memory leaks is to put memory metrics in another tool for further processing. Because this implementation is not sophisticated, the analysis will remain mostly a manual process.
Put this right above the setInterval call in the code:
To avoid putting heap allocation metrics in memory, let's opt to write to a CSV file for easy data consumption. This uses the async writeFile function with a callback. The callback is left empty to write to the file and continue without any further processing.
To grab gradual memory metrics, add this above the console.log :
With this code, you can debug memory leaks as heap utilization grows over time. You can use any tool that will analyze raw CSV data and show a nice visual.
If you're in a hurry and just want to see some data, Excel does the trick:
You can see a linear increase in memory usage over a short time with the 4.1GB limit. Memory consumption continues to grow and does not plateau, which shows there is a memory leak somewhere. When debugging these kinds of memory issues, look for code that causes the allocations to end up in the old generation. Objects that survive garbage collection likely hang around until the process dies.
One way to make this memory leak detection code more reusable is to wrap it around its own interval (since it does not have to live inside the main loop).
Keep in mind this is not production-ready but only shows how to debug memory leaks in local code. An actual implementation will include automatic visuals, alerting, and rotating the logs, so the server does not run out of disk space.
Избегайте утечек памяти
В этой статье объясняется, как работает управление памятью в JavaScript, и как избежать большинства возможных утечек.
Её содержание сводится к тому, что большинство утечек, которые можно отследить, вызваны неудалёнными ссылками на объекты, которые больше не нужны. Это может случиться, когда вы забыли удалить interval, timer или чрезмерно используете глобальные переменные.
«Heap out of memory» во время nmp install
Если во время установки пакетов с помощью npn или yarn у вас появляется эта ошибка, вы можете увеличить лимит памяти на время установки.
Плюсы:
- легкость и скорость написания
- легковесность
- относительная простота (в сравнении с java)
- npm (node package manager (огромное количество библиотек которые могут быть установлены в одну строку)
- каждая библиотека попадает в дерево зависимостей и это все делается легко
- постоянное развитие (сейчас активно развивается TypeScript (который привносит в JS типизацию, декораторы и используется например для Angular)
Expanding Memory Allocation Limits
The V8 garbage collector has a --max-old-space-size parameter available to the Node executable:
This sets the max limit to 8GB. Be careful when doing this. My laptop has ample room with 32GB. I recommend setting this to however much room is physically available in your RAM. Once physical memory runs out, the process starts to eat disk space via virtual memory. If you set the limit too high, you might find a way to damage your PC! The goal here is to avoid smoke coming out of the machine.
With 8GB to burn, test the new limit:
The heap size almost makes it to 8GB, but not quite. I suspect there is some overhead within the Node process to allocate this much memory. This time it takes 45.7 seconds for the process to die.
In production, it likely won’t take less than a minute to run out of memory. This is one reason why monitoring and having insight into memory consumption helps. Memory consumption can grow slowly over time, and it could take days before you know there is a problem. If the process keeps crashing and this 'heap out of memory' exception shows up in the logs, there might be a memory leak in the code.
The process might also chew on more memory because it is working with more data. If resource consumption continues to grow, it might be time to break this monolith into microservices. This will reduce memory pressure on a single process and allow nodes to scale horizontally.
Производительность, ресурсы, управление потоками, популярность и личный опыт
Недавно мы с коллегами обсуждали вопрос популярности некоторых технологий — в частности Java и node.js. После недолгого интернет-серфинга выяснилось, что именно эти технологии используют многие информационные гиганты для разработки и поддержания своих площадок в сети. Ниже, я приведу лишь малую часть.
Но все это лишь общая информация, касающаяся популярности. Информация, которая натолкнула меня на мысль углубиться в тему и порассуждать на предмет технических особенностей, что привело к написанию этой статьи.
Java — это язык, node.js — можно назвать экосистемой построенной на базе JS, и, прежде всего, на базе V8 — движка от Google.
Однако, когда мы говорим про Java, мы говорим не только про язык, а про виртуальную машину Java, а также всю экосистему и инфраструктуру построенную вокруг этой машины. Как минимум, их можно сравнивать по этому признаку — как результат, в обоих случаях, мы имеем среду исполнения. В случае Java — это виртуальная машина. В случае node.js — это движок V8 который представлен на большинстве ОС, таких как Windows, Linux, MacOS и менее известных.
Разработчики могут писать код пользуясь одним и тем же языком, и это будет работать более-менее одинаковым образом на разных ОС за счет того, что существует среда исполнения. Среда исполнения влияет на то, как происходит взаимодействие с ОС. Кроме того, их можно сравнивать т.к. они используются для решения похожего круга задач.
Когда в v8 попадает код JS, осуществляется just in time компиляция в байт код, который используется в виртуальной машине, код на JS выполняется все быстрее и быстрее.
Байт код — это промежуточный язык высокого уровня, поэтому в виртуальной машине Java пишут не только на Java, но также на Scala и Kotlin.
Есть предпосылки тому, что в ближайшем будущем для V8 можно будет использовать не только JS но и TypeScript или другие. На данный момент идет транспайлинг этих языков в JS. В будущем, они вероятно будут поддерживаться из коробки, и все будет работать намного быстрее.
Сейчас идет непрерывное развитие V8, и по большому счету, появление новых версий node.js связано с появлением новой версии движка V8. Они прямо взаимосвязаны.
Node.js был создан Райаном Далом (Ryan Dahl) в 2009 году.
Сам node.js включает в себя несколько основных составляющих:
- движок V8;
- библиотеку libuv, которая отвечает за центральную часть node — цикл событий (event loop), который осуществляет взаимодействие с ОС, а так же за асинхронный ввод/вывод (I/O);
- из набора различных JS библиотек и непосредственно самого языка JS.
Перейдем к его плюсам и минусам.
Перезапуск процессов
Допустим, ваша программа работает на компьютере с ограниченным объёмом памяти, например Raspberry Pi.
Мы будем использовать cluster и библиотеки node v8.
Cluster даёт возможность воспользоваться преимуществами многоядерных систем и запускать кластер из процессов Node.js.
V8 предоставляет API для конкретной версии V8, используемой в Node.js.
Давайте разделим программу на две сущности: master и worker.
Master будет перезапускать worker`ов в случае, если они перестанут работать из-за переполнения кучи. Worker`ы будут отвечать за основную логику (в нашем случае запускать тяжёлую функцию heavyHeapConsumer).
total_heap_size — размер кучи, который можно увеличить.
heap_size_limit — максимально возможный размер кучи.
В коде worker`а устанавливается total_heap_size равный 85% от heap_size_limit. Затем worker каждую секунду проверяет не превышен ли лимит. Если лимит превышен, то процесс worker убивает себя.
Лимит (85%) и интервал проверки (1 секунда) нужно выбирать для каждого конкретного случая. Здесь функция heavyHeapConsumer увеличивает кучу каждые 100мс. Если в вашем варианте увеличение будет происходить каждые 10мс, то следует уменьшить лимит и увеличить интервал проверки.
Ограничения памяти на разных версиях Node.js
Эти значения не объявлены официально, но с помощью небольшой программы можно получить такие значения для 64 битной архитектуры.
4GB памяти в куче будет достаточно для большинства случаев
Чтобы проверить лимит памяти вашей системы, создайте файл index.js и добавьте в него следующий код:
Diagnose Memory Limits with AppSignal's Magic Dashboard
The above shows that requests stopped for seven minutes around 14:25, and garbage collection was allowed to reduce memory pressure. The dashboard will also show when objects hang around old space for too long and cause a memory leak.
Эффективное использование кучи
В любом реальном приложении мы так или иначе будем использовать кучу, но с помощью следующих рекомендаций можно сделать работу с ней более эффективной:
1.По возможности копируйте объекты вместо того, чтобы передавать ссылки. Их передача возможна только в том случае, если объект крупный или операция копирования требует больших затрат.
2.По максимуму обходитесь без мутаций объекта. Вместо этого для их копирования используйте распространение объекта или Object.assign .
3.Вместо создания множественных ссылок на объект просто его скопируйте.
4.Используйте переменные с коротким жизненным циклом.
5.Старайтесь не создавать огромные деревья объектов. Если же это неизбежно, то обеспечьте им короткий жизненный цикл в локальной области видимости.
Минусы:
- гибкость и быстрое развитие порождает также и минусы т.к. надо постоянно следить за обновлениями, некоторые вещи выходят недостаточно протестированными;
- был случай, когда разработчик удалил свою библиотеку из NPM и множество приложений использующих ее перестали работать;
В противовес сразу рассмотрим основные характеристики Java.
Sum Up: Tackle Node.js Memory Limits and Leaks
In this post, we started by looking at what the V8 garbage collector does before exploring whether there are limits to heap memory and how to expand memory allocation limits.
Finally, we examined some potential tools to keep tabs on memory leaks in your Node.js app. We saw that memory allocation monitoring is possible by using crude tools like memoryUsage with some debugging techniques. Here, analysis remains a manual process.
Another alternative is to use professional tools like AppSignal, which offers monitoring, alerting, and nice visuals to diagnose memory problems in real-time.
I hope you've enjoyed this quick introduction to memory limits and diagnosing memory leaks.
P.S. If you liked this post, subscribe to our JavaScript Sorcery list for a monthly deep dive into more magical JavaScript tips and tricks.
P.P.S. If you need an APM for your Node.js app, go and check out the AppSignal APM for Node.js.
Would you like to contribute to the AppSignal blog? We're looking for skilled mid/senior-level Ruby, Elixir, and Node.js writers.
Самый быстрый способ — увеличить количество памяти в Node.js. Начиная с версии v8 вы можете устанавливать ограничение в мегабайтах с помощью флага --max-old-space-size :
Вы можете установить любое ограничение, но не используйте всю доступную память, иначе может произойти крэш системы.
Аналогичного эффекта можно добиться с помощью другого флага:
Грамотное использование замыканий, таймеров и обработчиков событий
Как уже было отмечено ранее, замыкания, таймеры и обработчики событий — это те области, где могут произойти утечки памяти. Начнем с замыканий, как наиболее часто встречающихся в коде JavaScript. Посмотрим на следующий код команды Meteor, который приводит к утечке памяти, так как переменная longStr не подлежит удалению и увеличивает объём занимаемой памяти.
Выше обозначенный код создает несколько замыканий, которые удерживают ссылки на объекты. В этом случае устранение утечки памяти возможно путем определения originalThing как null в конце выполнения функции replaceThing . Подобных ситуаций тоже можно избежать, создавая копии объектов и соблюдая выше описанный немутабельный подход.
Когда дело касается таймеров, всегда передавайте копии объектов и обходитесь без мутаций. По окончании работы таймеров проводите их очистку с помощью методов clearTimeout и clearInterval .
Тоже самое относится к слушателям событий и наблюдателям. Как только они выполнили свою задачу, очистите их. Не оставляйте слушателей в постоянно работающем состоянии, особенно если они удерживают ссылки на объекты из родительской области видимости.
В настоящее время утечки памяти в JavaScript не являются такой уж большой проблемой, как бывало раньше, благодаря эволюции движков JS и улучшениям в языке. Но если мы не будем внимательны, то они по-прежнему будут происходить и приводить к снижению производительности и сбоям в работе приложения/ОС.
Во-первых, чтобы убедиться, что наш код не приводит к утечкам памяти в приложении Node.js, нужно разобраться в принципах ее управления движком V8. Во-вторых, важно понять, что послужило их причиной.
После этого в наших силах избежать развития таких ситуаций. Но если все-таки мы обнаружим утечку памяти/проблемы с производительностью, то теперь будем знать, что искать.
Эффективное использование стека
Максимально возможное использование переменных стека способствует эффективной и производительной работе памяти, так как доступ к стеку происходит гораздо быстрее, чем к куче. Это также позволяет избежать случайных утечек памяти.
Конечно, использование исключительно статических данных непрактично. В реальных приложениях нам приходится работать со многими объектами и динамическими данными. Но мы можем оптимизировать применение переменных стека при помощи ряда приемов:
1.Избегайте использования ссылок переменных стека на объекты кучи по мере возможности и не храните неиспользуемые переменные;
2.Деструктуризируйте и используйте только необходимые поля объектов или массивов вместо того, чтобы целиком передавать их функциям, замыканиям, таймерам и обработчикам событий. Тогда ссылки на объекты уже не будут оставаться в замыканиях. Передаваемые поля могут быть в основном примитивами, которые будут храниться в стеке.
An Introduction to V8 Garbage Collection
First, a bit of an introduction to the V8 garbage collector. The heap is where memory allocations go, and it is divided up into several generational regions. The regions are simply called generations, and objects belong to a generation as they age throughout their lifetime.
There is a young generation and an old generation. Young objects are further split up into nursery and intermediate sub-generations. As objects survive garbage collection, they join the older generation.
The basic principle in the generational hypothesis is that most objects die young. The V8 garbage collector is designed to exploit this fact and only promotes objects that survive garbage collection. As objects get copied into adjacent regions, they eventually end up in an old generation.
There are three major areas in Node memory consumption:
- Code - where the code that's executed goes
- Call stack - for functions and local variables with primitive types like number, string, or boolean
- Heap memory
Heap memory is our main focus today.
Now that you know more about the garbage collector, it's time to allocate some memory on the heap!
Local variables die young and as soon as the function call ends within the call stack. Primitives like numbers never make it to the heap and are allocated in the call stack instead. The object arr will go in the heap and likely survive garbage collection.
Минусы:
- тяжеловесность,
- некоторые парадигмы Java создавались давно и уже устарели,
- JDK проприетарен, поэтому Java развивается медленно.
В последнее время JS начинает обгонять Java (и чем дальше, тем больше).
Также Java уходит из мира Android, ей на смену приходит Kotlin который хоть и использует JVM, но все же является другим языком.
Java была создана компанией Sun, которая позже была выкуплена компанией Oracle и по сей день принадлежит ей. По этой причине, для многих компаний использование Java создает некоторые проблемы.
У Google возникли проблемы, когда Oracle начали с ними судебное разбирательство за использование Java в Android. Из-за этого Google очень активно принял Kotlin, который появился независимо. Java является проприетарной. Но есть виртуальная машина Oracle, а также открытая виртуальная машина Java (open JVM), которая используется в Linux и написана в open source. Иногда существуют некоторые несовместимости, но в последнее время их все меньше и меньше.
Кстати, Google так и не смог полностью отказаться от Java. В Dalvik, который используется как ядро в Android, вшит JVM. Возможно от этого уйдут, но сделать это будет очень сложно т.к. практически вся экосистема Android построена на Java — прежде всего — на использовании модернизированного JVM. И это, в какой-то момент, тоже было причиной конфликта между Oracle и Google, потому что Oracle запрещает просто так модернизировать JVM. Это самая важная часть Java. А сам язык можно использовать практически без ограничений.
Прежде всего стоит отметить, что производительность Java намного выше чем у JS, и, соответственно, node.js.
Если запустить какую-то простую задачу, вроде возведения в квадрат, то в тестах показатели могут различаться до 10 раз. Если запустить циклы в миллионы задач калькуляции, Java практически всегда будет превосходить node.js. Плюс, огромное различие между Java и node.js в том, что node является однопоточным, это является как его преимуществом, так и недостатком с другой стороны.
Java умеет работать с потоками, которые поддерживаются на уровне ОС, и получается, что программа написанная на Java наиболее полно использует возможности ОС. И если нужно написать высоконагруженное приложение, которое будет использовать большое количество вычислений, то Java для этого однозначно подойдет лучше. Проблема в том, что даже маленький сервер написанный на Java будет занимать много памяти — как на диске, так и оперативной.
Node.js является легковесным за счет архитектуры построенной на обработке событий. Он построен для работы в качестве веб-сервера и очень хорошо справляется с обслуживанием легковесных задач. Например, простой запрос вроде расчета чего-либо, или записи в базу данных происходит очень быстро. А если запросов становится очень много и мы хотим масштабировать систему в node, можно использовать веб-сервер Nginx или Apache. Можно завести много одинаковых инстансов node. Тогда все будет распределяться через балансировку нагрузки по round-robin. Если мы запустим 8 инстансов node на 16 ядер соответственно, ОС сама распределит инстансы между ядрами. Node этим не управляет, у него будет один поток.
В Java мы можем создать приложение и запустить в нем 8 потоков. За счет того, что происходит более тесное взаимодействие с ОС, можно распределить нагрузку.
Как известно, один из веб-серверов написанных на Java — это tomcat. Там можно четко проследить, что когда пользователь делает запрос, запускаются дополнительные потоки. А когда приходит запрос на node, цикл событий (event loop) будет обработан и отправлен обратно, затем придет следующий запрос. И за счет того, что мы не ждем результатов первого, он тоже будет подхвачен. Пока запросы легковесные, все хорошо. Однако, когда производится тяжелое вычисление, при наличии одного инстанса, node останавливается и наступает тайм-аут.
На node можно прописать буквально несколько строк кода и получить простейший веб-сервер. Естественно, для более широкого функционала, где будут нотификации, авторизации, логирование и т.д. это сложнее реализовать, но существуют фреймворки которые позволяют решать такие вопросы.
На Java есть развитое API — concurrency api, которое позволяет работать с конкурентными потоками. Но в то же время, это является и одной из проблем т.к. конкурентность это очень сложная штука и далеко не каждый разработчик хорошо в этом разбирается.
Веб, REST API — это стихия node, и иногда именно его и используют. Но если мы имеем дело со сложными калькуляциями, все же лучше использовать Java.
На Java у меня был интересный проект — распределенное приложение, основной задачей которого была обработка больших объемов графической информации для дальнейшего использования в каталогах. При создании каталога необходимо подготовить наборы большого количества изображений различных разрешений, которые будут использованы при создании каталога. Проще говоря — это приложение для автоматизации предпечатной подготовки каталога.
Раньше фотографам приходилось делать все вручную. Для начала нужно было использовать какое-то небольшое приложение для того, чтобы загрузить свои изображения. Далее, специалист создававший каталог должен был разработать структуру каталога через другое приложение. Потом, в другом приложении, создавался воркфлоу, который раскидывал картинки в ту структуру, которая была создана. В общем, процесс был довольно тяжелым. Использовался ImageMagick который есть на Linux, Windows, MacOS. Мы имели дело с Linux.
Например, в приложение была загружена .tiff картинка размером 200–300 мб, а из нее нужно было сделать картинки различных разрешений, что-то вырезать, или сделать подложку.
Первая версия приложения не справлялась с большой нагрузкой, не хватало даже сервера с 16 ядерным процессором. Мы улучшили архитектуру приложения для использования нескольких инстансов одновременно, чтобы кардинально не менять работу приложения. Запускалось много инстансов, которые взаимодействовали между собой и каждый из них обрабатывал кусок задачи. Было сложно, но нам удалось успешно реализовать все буквально за пару месяцев. И система до сих пор работает. В процессе пришлось разбираться с конкурентностью и различными аспектами взаимодействия.
Что-то из этого проекта все же можно было перенести на node, но некоторые вещи все равно пришлось бы делать на Java, т.к. там было много различных вычислений. В принципе, мы могли сделать части на node, которые бы вызывали определенные части на Java и использовать микросервисную архитектуру. Можно было использовать смешанный вариант. Но этот подход не всегда работает, т.к. разработчик специализирующийся на node, может не являться специалистом в Java и наоборот. А найти универсальных разработчиков гораздо сложнее.
Был проект по организации большого объема данных. Чем-то похоже на проект описанный выше. Только здесь у нас загружается файл, который содержит большой набор информации и подлежит валидации через сторонний сервис (написанный на java), несколько раз и по разным правилам. Нужно было обрабатывать сотни гигабайт информации, а node для этого не предназначен.
И если изначально приложение обрабатывало файл, содержащий 10000 записей, то теперь может обрабатывать до миллиона. Нам все же удалось решить эту задачу с помощью node.js, хотя на Java ее можно было решить проще, однако заказчик хотел использовать именно node, т.к. нужна была единая инфраструктура и архитектура с микросервисами написанными на JS. Используя node решить задачу было намного сложнее, и потребовало больше времени, но node.js выигрывает за счет скалируемости. Именно поэтому сейчас мы можем наращивать количество воркеров и обрабатывать все больше и больше данных. На Java это происходило бы сложнее.
На самом деле, любую задачу можно решить и так и так, но тут стоит повториться: если есть много вычислений то лучше использовать Java, если вычислений не много, можно смело использовать node.
Сейчас идет к тому, что node.js часто будет использоваться как обертка, а начинку будут писать на других языках. Его недостатки давно известны. Например, такой условный недостаток как однопоточность уже исправлен. В последней версии node представлена возможность использования нескольких потоков.
Java изначально создавалась как легковесное решение заменяющее C++, а теперь стала тяжеловесной. Это как эволюция. Возможно, когда-нибудь появится что-то, что заменит и node.
Сейчас, по количеству заказов, и по моим ощущениям, node.js уже обогнал Java.
JS активно развивается и он будет меняться — возможно что-то придет ему на смену.
Сейчас не видно потенциального конкурента, который смог бы заменить Java и node.js.
Проблема в том, что развитие Java в последнее время идет довольно медленно, а node.js развивается с такой скоростью, что заменить его в ближайшее время не представляется возможным.
In this post, we will explore memory heap allocation in Node and push local hardware to its limit. Then we will find practical ways to monitor Node processes to debug memory issues.
Ready? Let’s get going!
Сокращение использования глобальных переменных
Поскольку глобальные переменные не подвергаются сборке мусора, то лучше всего убедиться, что вы не злоупотребляете их использованием. Ниже речь пойдет о том, как это сделать.
Обходимся без случайных глобальных переменных
Когда вы присваиваете значение необъявленной переменной, JavaScript по умолчанию определяет ее как глобальную. Это может произойти по ошибке и привести к утечке памяти. Подобное может случиться в результате присвоения переменной к this .
Во избежание таких сюрпризов, всегда пишите код JavaScript в режиме strict и используйте нотацию 'use strict'; в начале файла JS.
В режиме strict выше приведенный пример приведет к ошибке. Однако при использовании ES модулей и компиляторов, таких как TypeScript или Babel, нет необходимости включать данный режим, так как он будет задействован по умолчанию. В последних версиях Node.js вы можете активировать режим strict глобально, сопроводив выполнение команды node флагом --use_strict .
И наконец, помните, что не следует привязывать глобальное this к функциям, использующим методы bind или call , так как это лишает использование режима strict всякого смысла.
Профилирование
Профилирование помогает обнаружить утечки памяти. На фронтенде это можно сделать в Chrome в Инструментах разработчика во вкладке Memory.
В Node.js начиная с версии 6.3.0 также можно использовать Chrome для отладки использования памяти.
Во-первых, запустите приложение в режиме проверки:
Затем откройте страницу в Chrome, введите адрес chrome://inspect и нажмите на кнопку Open dedicated DevTools for Node.
После этого откроется окно, в котором вы сможете подключиться к вашему Node.js приложению.
Are There Any Limits to Heap Memory?
Now for a test of bravery — push the Node process to its maximum capacity and see where it runs out of heap memory:
This allocates around 10 megabytes with an interval of 40 milliseconds, which gives enough time for garbage collection to promote surviving objects to the old generation. process.memoryUsage is a crude tool that gathers metrics around heap utilization. As heap allocations grow, the heapUsed field tracks the size of the heap. This heap field reports the number of bytes in RAM, which can be converted to gigabytes.
Your results may vary. A Windows 10 laptop with 32GB of memory yields this result:
Here, the garbage collector attempts to compact memory as a last resort before giving up and throwing a 'heap out of memory' exception. This process hits a 4.1GB limit and takes 26.6 seconds to realize it was time to die.
The reasons for this are somewhat unknown. The V8 garbage collector originally ran in a 32-bit browser process with strict memory restrictions. These results suggest that the memory limit might have carried over from legacy code.
At the time of writing, the script ran under the latest LTS Node version and is using a 64-bit executable. Theoretically, a 64-bit process should be able to allocate more than 4GB and grow comfortably well into 16 terabytes of address space.
Keep Track of Node.js Memory Leaks in Production
Although the above code isn't viable for production environments, we've seen how to debug some memory leaks. So, as an alternative, the Node process can be wrapped around a daemon process like PM2.
Set a restart strategy when memory consumption hits a limit:
Units can be K (kilobyte), M (megabyte), and G (gigabyte). It takes about 30 seconds before the process restarts, so have multiple nodes via a load balancer to avoid outages.
Another nifty tool is the platform-independent native module node-memwatch which fires an event when it detects a memory leak in running code.
The event is emitted via leak , and the callback object has a reason with the heap growth over consecutive garbage collections.
Читайте также: