Содержание


Введение в мультиарендность для виртуальных машин Java

Новая функция для облачных систем в бета-релизе IBM Java 8

Comments

Поставщикам облачных сервисов необходимо сопоставлять затраты на инфраструктуру, которая требуется для функционирования системы и предоставления этих сервисов, с получаемыми в результате выгодами. Необходимость сопоставления затрат и выгод побуждает поставщиков рассматривать различные архитектуры. Возможные варианты охватывают спектр от архитектур без совместного использования до совместно используемых архитектур с несколькими арендаторами. В варианте без совместного использования поставщик предлагает аппаратные средства, программное обеспечение и приложения, полностью выделенные отдельным клиентам. В варианте мультиаренды с совместным использованием приложения нескольких клиентов поддерживаются средствами единственного приложения, а все обеспечивающие аппаратные и программные средства используются совместно.

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

Для Java-арендаторов в архитектурном спектре имеется две позиции –JVM-машины с совместным использованием или JVM-машины без совместного использования. В любой архитектуре, в которой приложение верхнего уровня используется совместно, JVM-машина должна быть совместно используемой. Совместное использование JVM экономит ресурсы памяти и процессорные ресурсы. Однако в случае традиционной технологии JVM совместное использование JVM-машины в общем случае устраняет на уровне инфраструктуры любую остававшуюся на нем изоляцию, в результате чего эту изоляцию должно обеспечивать само приложение верхнего уровня. В данной статье описывается функциональность мультиарендности, представленная для пробного использования в последнем релизе Java 8 beta от IBM (см. раздел Ресурсы). Эта опция позволяет при развертывании воспользоваться преимуществами совместного использования JVM-машины, но при этом поддерживает более высокий уровень изоляции, чем можно достигнуть при совместном использовании традиционной JVM-машины.

Издержки и выгоды мультиарендной JVM

Основное преимущество мультиарендной JVM состоит в отсутствии потребления памяти, которое обычно имеет место при развертывании с использованием нескольких стандартных JVM. Эти накладные расходы в виде потребления памяти обусловлены несколькими причинами.

  • Куча Java потребляет сотни мегабайт памяти. Объекты кучи не могут совместно использоваться несколькими JVM-машинами, даже если эти объекты идентичны. Более того, JVM-машины имеют тенденцию использовать всю выделенную им кучу, даже если пиковый объем памяти требуется им лишь на протяжении короткого промежутка времени.
  • JIT-компилятор (Just-in-time) потребляет десятки мегабайт памяти, поскольку генерируемый им код является приватным и потребляет память. Кроме того, генерация этого кода требует существенных процессорных ресурсов, которые отбираются у приложений.
  • Внутренние артефакты для классов (многие из которых, такие как String и Hashtable, существуют для всех приложений) потребляют память. Для каждой JVM-машины существует по одному экземпляру каждого из этих артефактов.
  • По умолчанию каждая JVM имеет по одному потоку хелпера-сборщика мусора на каждое ядро, а также несколько потоков компиляции. Действия в сфере компиляции или сборки мусора могут происходить одновременно в одной или в нескольких JVM, что приводит к снижению оптимальности, поскольку JVM-машины конкурируют за ограниченное процессорное время.

Помимо снижения затрат на память и на обработку мультиарендная JVM-машина обеспечивает более высокий уровень изоляции, чем исполнение нескольких приложений в одной традиционной JVM-машине.

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

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

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

Использование мультиарендной JVM

Чтобы использовать вариант с совместным использованием среды исполнения вместе с другими арендаторами, пользователь приложения при его запуске добавляет к командной строке один аргумент -Xmt. Пример:

java -Xmt -jar one.jar

В результате приложение будет вести себя точно так же (с учетом ограничений, которые мы опишем ниже), как при исполнении на выделенной JVM-машине. Однако в действительности оно исполняется вместе с другими приложениями. Расширения в мультиарендной JVM делают возможным такой способ исполнения и обеспечивают изоляцию между арендаторами, которые совместно используют одну и ту же JVM-машину.

После запуска арендатора модуль запуска JVM-машины (JVM launcher) определяет местоположение демона совместно используемой JVM-машины (javad) или запускает его в случае необходимости (см. рис. 1).

Рисунок 1. Модуль запуска JVM автоматически находит (и в случае необходимости, запускает) демон совместно используемой JVM-машины
Screen capture and diagram represents the JVM launcher locating and starting the shared JVM daemon (javad)
Screen capture and diagram represents the JVM launcher locating and starting the shared JVM daemon (javad)

При запуске второго арендатора этот арендатор находит демон существующей совместно используемой JVM-машины и исполняется в этой JVM-машине (см. рис. 2).

Рисунок 2. JVM launcher находит демона существующей JVM-машины и подключается к нему
Screen capture and diagram represents the JVM launcher locating and connecting to the existing JVM daemon (javad)
Screen capture and diagram represents the JVM launcher locating and connecting to the existing JVM daemon (javad)

Результат состоит в том, что в процессе javad "живет" одна копия кода начальной загрузки (bootstrap), которая является общей для обоих арендаторов. Такая организация позволяет арендаторам совместно использовать большую часть структур среды исполнения.

Запуск существующих приложений с использованием мультиарендной JVM осуществляется весьма просто – требуются лишь некоторые изменения в командной строке.

Реализация изоляции

В нормальных условиях два или несколько приложений, работающие в одной и той же (обычной) JVM-машине, не изолированы друг от друга. Деятельность каждого такого приложения будет оказывать влияние на возможности другого приложения. Кроме того, данные, доступные для совместного использования через статические поля, будут доступны всем приложениям. В режиме мультиарендной JVM эти проблемы решаются следующими двумя способами: изоляция статических полей и ограничение ресурсов.

Изоляция статических полей

В мультиарендной JVM арендаторы совместно используют инвариантные части классов. К числу этих частей относятся: скомпилированный код для методов, структуры данных, которые использует JVM-машина, а также другие подобные артефакты. Такое совместное использование обеспечивает экономию памяти, поскольку отпадает необходимость в отдельных копиях этих частей, которые существовали бы в случае использования нескольких JVM. В то же время мультиарендная JVM предоставляет каждому арендатору собственную копию статических полей. Вследствие изоляции статических полей — а также того факта, что каждый арендатор в общем случае может получить доступ лишь к тем экземплярам объектов, которые он сам создал, — каждый арендатор имеет возможность обращаться только к тем данным, которые ассоциированы именно с ним. Результатом является изоляция данных между арендаторами.

Ограничение ресурсов: противодействие плохому поведению

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

  • Процессорное время
  • Размер кучи
  • Количество потоков
  • Ввод/вывод файлов: пропускная способность по чтению, пропускная способность по записи
  • Ввод/вывод сокета: пропускная способность по чтению, пропускная способность по записи

Эти параметры управления можно задать в командной строке с аргументом -Xmt. Примеры.

  • -Xlimit:cpu=10-30 (процессорное время: 10% минимум, 30% максимум)
  • -Xlimit:cpu=30 (процессорное время: 30% максимум)
  • -Xlimit:netIO=20M (пропускная способность: 20 Мбит/с максимум)
  • -Xms8m-Xmx64m • (куча: 8 МБ исходные размеры, 64 МБ максимум)

Документация по Java 8 содержит информацию по всем доступным параметрам (см. раздел Ресурсы).

Производительность и потребление ресурсов

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

В таблице 1 и таблице 2 показаны результаты, полученные при использовании машины с 1 ГБ памяти и с 64-разрядной JVM (JVM-машина со сжатыми ссылками, со сбалансированной политикой сборки мусора во всех экземплярах). Столбец Ручная настройка в обеих таблицах демонстрирует результаты использования обычной JVM-машины после того, как мы в ручном режиме настроили параметры командной строки на достижение максимально возможной плотности (таблица 1) или максимально возможной скорости запуска (таблица 2). В столбце По умолчанию показаны результаты для обычной JVM-машины с опциями по умолчанию.

У мультиарендной JVM-машины плотность в 1,2 – 4,9 раза выше, чему у JVM без совместного использования в зависимости от конкретного приложения (таблица 1).

Максимальное количество одновременно исполняемых приложений
ПриложениеОписаниеМультиаренднаяРучная настройкаПо умолчаниюУлучшение в случае мультиарендной JVM
Hello World Печатает слова HelloWorld, а затем засыпает3097363В 4,2 – 4,9 раз
JettyЗапускает Jetty и ждет запросов34-18В 1,9 раза
TomcatЗапускает Tomcat и ждет запросов28-13В 2,1 раза
JRubyЗапускает JRuby и ждет запросов322615В 1,2 – 2,1 раз

Увеличение плотности является результатом совместного использования ключевых артефактов:

  • Классы и связанные артефакты, загружаемые при начальной загрузке и при загрузке классов расширения; объект кучи Class для каждого класса, загружаемого соответствующим загрузчиком; объекты кучи, которые арендаторы могут безопасно использовать в совместном режиме (например, интернированные объекты String).
  • Скомпилированный JIT-компилятором код и метаданные для скомпилированных JIT-компилятором классов.
  • Куча: один арендатор может использовать все пространство, доступное в куче, если оно не требуется другим арендаторам.

В таблице 2 показано, что в случае мультиарендной JVM мы в 1,2 – 6 раз сократили среднюю продолжительность запуска.

Таблица 2. Продолжительность запуска (первый запуск/в среднем)
ПриложениеОписаниеМультиаренднаяРучная настройкаПо умолчаниюУлучшение в случае мультиарендной JVM
Hello WorldПечатает слова HelloWorld, а затем засыпает5709/138 мс514/400 мс3361/460 мсВ 3,3 раза
JettyЗапускает Jetty и ждет запросов7478/2116 мс-6296/12624 мсВ 6 раз
TomcatЗапускает Tomcat и ждет запросов9333/6005 мс-7802/7432 мсВ 1,2 раза
JRubyЗапускает JRuby и ждет запросов12391/3277 мс14847/4101 мс7849/6058 мсВ 1,25 – 1,8 раза

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

Эти первые результаты получены с использованием JVM-машин, находящихся в стадии разработки; поэтому со временем они будут дополнительно улучшены. Более того, эти примеры не распространяются на совместное использование в тех случаях, когда приложения нуждаются в ресурсах в разные моменты времени. В типичной JVM-среде занимаемое пространство памяти для каждой JVM-машины имеет тенденцию к росту вплоть до максимального значения, который требуется ей на протяжении жизненного цикла. В стандартной JVM большая часть этого пространства не используется совместно. В случае мультиарендной JVM – при условии, что потребности в ресурсах не перекрываются – совместное использование памяти для кучи и для собственных артефактов существенно упрощается.

Ограничения

Одна из целей разработки мультиарендной JVM — предоставить возможность запуска всех Java-приложений без их модификации. На данный момент это невозможно вследствие ограничений, налагаемых спецификациями Java, и ограничений в нашей текущей реализации. Важнейшие известные ограничения:

  • Интерфейс JNI (Java Native Interface) и нативные артефакты. Опция multitenant JVM не обеспечивает изоляцию для т.н. нативных артефактов при использовании интерфейса JNI. Приложения с предоставленными пользователем JNI-вызовами не могут безопасно исполняться на мультиарендной JVM-машине. Такие приложения способны влиять на функционирование всей JVM и на доступ к данным со стороны других арендаторов. В случае, когда к нативным артефактам имеется достаточная степень доверия (например, речь идет об известном программном продукте связующего уровня), этот риск может оказаться приемлемым. Кроме того, операционная система позволяет процессу совместно используемой JVM-машины загружать только одну копию совместно используемой библиотеки, в которой расположены нативные артефакты. В результате несколько арендаторов не смогут загружать одни и те же нативные артефакты, если те находятся в одной совместно используемой библиотеке.
  • Интерфейс JVMTI (Java Virtual Machine Tool Interface). Поскольку деятельность по отладке и профилированию влияет на всех арендаторов, совместно использующих соответствующий JVM-сервер, эти функции в настоящий момент не поддерживаются в мультиарендном JDK. Мы планируем продолжить работу в этом направлении.
  • Программы для поддержки графических интерфейсов пользователя. Библиотеки, такие как SWT, поддерживают глобальное состояние на нативном уровне и, соответственно, не могут быть задействованы в мультиарендном JDK.

Заключение

В данной статье была представлена мультиарендная JVM-машина (multitenant JVM), описан порядок ее использования и рассмотрены сопряженные с таким использованием издержки и выгоды. Мы надеемся, что вас заинтересует эта тематика и что вы испытаете бета-релиз IBM Java 8 и сообщите нам свое мнение. Мы уверены в том, что мультиарендная JVM-машина может дать существенные преимущества при использовании в соответствующих средах.


Ресурсы для скачивания


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Облачные вычисления
ArticleID=964555
ArticleTitle=Введение в мультиарендность для виртуальных машин Java
publish-date=02032014