Pull to refresh

Архитектура памяти: Erlang против Java

Reading time4 min
Views12K
Original author: Byron (javacodegeeks.com)
Я прочитал очень-очень интересную статью «Стратегии управления памятью для Erlang VM». Она была написана в качестве диссертации Джеспером Вильхельмсоном. Я подумал, что было бы неплохо обсудить различия между управлением памятью в Erlang и Java VM от Oracle.

Очень краткое введение для тех, кто никогда не слышал о Erlang: это функциональный язык, который использует асинхронную передачу сообщений в качестве основы для параллелизма. Для передачи сообщений используется семантика копирования, что делает, по существу, прозрачным для программиста распределение нескольких Erlang VM, работающих на более чем одной машине.

Erlang и Java похожи в в том смысле, что оба используют виртуальную машину для абстрагирования от аппаратного слоя. Оба языка используют машинно-независимый байт-код. Оба являются runtime-системами со «сборкой мусора», освобождающими программистов от ручного управления памятью.

Накладные расходы (в оригинале — overhead) для потоков в Erlang — очень низкие. Мне кажется, что для Erlang-потока требуется около 512 байт. Для Java-потоков, как правило, необходимо около 512 килобайт, что примерно в 1000 раз больше. Системы с множеством потоков, которые работают асинхронно должны быть хорошо продуманны программистом. Типичные Erlang-системы держат тысячи или десятки тысяч потоков. При этом никакого дураковаляния со threadpool'ами и executor'ами, которым мы занимаемся в Java.

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

Но этот пост не о модели программирования в Erlang. Он о том, как Erlang VM работает с памятью.

Виртуальная машина Java использует то, что Erlang программист назвал бы общей топологией кучи. Существует одна большая куча, которая используется всеми потоками. Большая часть памяти выделяется в этой куче. В дополнение к куче, JVM использует некоторые специализированные области данных, такие как кэш кода и permanent generation. Они также делятся между всеми потоками.

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

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

Куча сообщений предназначена для данных, используемых в сообщениях. Сообщения также распределяются между процессами. Сообщения передаются между потоками путем копирования указателя от потока-отправителя потоку-адресату. Данные же сообщения хранятся в Куче сообщений.

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

Например, тот простой факт, что кучи являются закрытыми избавляет от появления взаимных блокировок, а, следовательно, и от проверки на них.

В последней версии Erlang VM сделан еще один шаг вперед — возможность иметь более одного планировщика. Один планировщик на каждый физический процессор, если быть точным. Это также избавляет от проверок на целый класс блокировок. Когда планировщик простаивает, он может получить некоторые процессы от другого планировщика.

Java есть чему поучиться у Erlang. Тем не менее, есть несколько хороших вещей в Java, по которым я скучаю при работе с большими Erlang-системами.

Erlang VM будет перераспределять кучи, когда поток накапливает большое количество данных. Однако, при действии алгоритма перераспределения почему-то размеры кучи быстро растут. При высокой нагрузке, я видел, что Erlang VM съедает 16 Гб оперативной памяти в считанные минуты. Каждый релиз должен быть тщательно проверен в нагрузочном тестировании, чтобы он имел адекватные требования к памяти.

Пока нет механизмов в Erlang VM, позволяющих обуздать рост памяти. Виртуальная машина радостно выделяет так много памяти, что система бежит в своп и исчерпывает всю виртуальную память. Это может привести машину к «зависанию» даже при доступе через консоль KVM. В прошлом мы должны были перегружать машины, чтобы вновь получить к ним доступ.

Основанная на очередях модель программирования Erlang позволяет с большим удовольствием писать код, но с другой стороны — это ахиллесова пята в продакшне. Каждая очередь в Erlang является неограниченной. Виртуальная машина не будет бросать исключения или ограничения количества сообщений в очереди. Иногда процесс останавливает обработку сообщений в связи с ошибкой, или процесс просто не может успеть за потоком сообщений направляемых к нему. В этом случае, Erlang просто позволяет очереди сообщений для этого процесса расти, пока VM не будет убита или пока не блокируется машина, что случается раньше.

Это означает, что при запуске «больших» Erlang VM в продакшн-среде вам нужна проверка на уровне операционной системы, которая будет убивать процесс, если используется слишком много памяти.

Таким образом, я считаю, что модель памяти Erlang с частными кучами может быть очень мощным инструментом. Она избавляет от целых классов механизмов блокировок во время выполнения и, что означает, что она будет масштабироваться лучше, чем Java. С другой стороны, жесткие ограничения Java на память дают выигрыш, когда ваша система будет зафлужена или будет под DDoS'ом.

Ну, и на последок:

Существуют параметры командной строки для Erlang VM, позволяющие перейти от использования топологии частной кучи к использованию топологии общей кучи.

Мне нравятся Erlang и Java. Их трудно сравнивать, потому что слишком мало общего для разработчика. В общем, всё же для большинства систем я хотел бы использовать Java. У неё лучшая поддержка различных инструментов и число доступных библиотек просто ошеломляет. Я выбираю Erlang в случае, когда нужна поток-ориентированная системы обмена сообщениями. Вот где Erlang-модель программирования действительно оказывается великолепной.

Ссылки:
Erlang memory architecture vs Java memory architecture from our JCG partner Kees Jan Koster at Java-Monitor


Счастливого кодирования! Не забывайте делиться!
Byron

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
По ссылке на оригинал есть интересные (и опровергающие) комментарии, заинтересованным читать обязательно!
Tags:
Hubs:
Total votes 46: ↑42 and ↓4+38
Comments40

Articles