Что делает горячее развертывание "сложной проблемой"?

На работе у нас возникла проблема с исключениями " PermGen out of memory ", и руководитель группы решил, что это ошибка в JVM - что-то, связанное с горячим развертыванием кода. Не объясняя многих деталей, он указал, что горячее развертывание - это "сложная проблема", настолько сложная, что даже.NET пока этого не делает.

Я нашел много статей, объясняющих горячее развертывание с высоты птичьего полета, но всегда испытывал недостаток в технических деталях. Может ли кто-нибудь указать мне техническое объяснение и объяснить, почему горячее развертывание является "сложной проблемой"?

4 ответа

Решение

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

Я полагаю, что часть проблемы связана с тем, должен ли GC удалять старые экземпляры Class из perm gen или нет. Как правило, каждый раз, когда вы выполняете горячее развертывание, новые экземпляры классов добавляются в пул памяти PermGen, а старые, которые теперь не используются, обычно не удаляются. По умолчанию виртуальные машины Sun не запускают сборку мусора в PermGen, но это можно включить с помощью необязательных аргументов команды "java".

Таким образом, если вы развернете достаточно много раз, вы в конечном итоге исчерпаете пространство PermGen.

Если ваше веб-приложение не закрывается полностью, когда оно не развернуто - например, если оно оставляет работающий поток - тогда все экземпляры Class, используемые этим веб-приложением, будут закреплены в пространстве PermGen. Вы повторно развернули и теперь у вас есть еще одна полная копия всех этих экземпляров Class, загруженная в PermGen. Вы удаляете, и поток продолжает работать, закрепляя ДРУГОЙ набор экземпляров классов в PermGen. Вы перераспределяете и загружаете целый набор копий... и в итоге ваш PermGen заполняется.

Иногда это можно исправить с помощью:

  • Предоставление аргументов команды для недавней Sun JVM для включения GC в PermGen и классов. То есть: -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
  • Использование другой JVM, в которой не используется PermGen фиксированного размера или GC для загруженных классов.

Но это поможет, только если ваше веб-приложение завершит работу полностью и чисто, не оставив живых ссылок ни на один из экземпляров Class какого-либо класса, загруженного загрузчиками классов для этого веб-приложения.

Даже это не обязательно решит проблему из-за утечек из загрузчика классов. (А также слишком много интернированных строк в некоторых случаях.)

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

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

Конечно, Java с самого начала поддерживала динамическую загрузку классов, но трудно перезагружать классы.

Было сочтено вредным (и по уважительной причине), что запущенное Java-приложение было внедрено с новым классом с вредоносным кодом. Например, взломанная реализация java.lang.String, поступающая из Интернета, которая вместо создания строки удаляет некоторый случайный файл, вызывая метод length().

Таким образом, способ, которым Java был задуман (и я предполагаю, что.NET CLR в последствии, потому что он был очень "вдохновлен" в JVM), состоял в том, чтобы предотвратить загрузку уже загруженного класса той же самой ВМ.

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

Например, я использовал загрузчики классов, которые загружают классы из LDAP или RDBMS

Горячее развертывание становится необходимостью в мире Java, когда сервер приложений стал основным для Java EE (а также создает потребность в микроконтейнерах, таких как spring, чтобы избежать такого рода нагрузки).

Перезапуск всего сервера приложений после каждой компиляции сводит с ума любого.

Таким образом, провайдер сервера приложений, предлагает этот "пользовательский" загрузчик классов, чтобы помочь горячему развертыванию, и используя файл конфигурации, такое поведение ДОЛЖНО быть отключено при установке в производство. Но вы должны использовать тонны памяти при разработке. Поэтому хороший способ сделать это - перезапускать каждые 3 - 4 развертывания.

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

Например, в Ruby вы можете даже добавить методы к запущенному классу, переопределить метод во время выполнения или даже добавить один метод к уникальному конкретному объекту.

Компромисс в таких средах - это, конечно, память и скорость.

Надеюсь, это поможет.

РЕДАКТИРОВАТЬ

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

Это JavaRebel от ZeroTurnaround

В Sun JVM исправлено пространство PermGen, и в конечном итоге оно все используется (да, по-видимому, из-за ошибки в коде, связанном с загрузчиком классов) => OOM.

Если вы можете использовать JVM другого поставщика (например, Weblogic), он динамически расширяет пространство PermGen, поэтому вы никогда не получите OOM, связанный с permgen.

Какую версию Java вы используете? В ранней версии Sun 1.4.2 были ошибки, но они работали очень долго.
Кстати, как вы будете сообщать новости руководителю вашей команды? Вы руководите командой?

Другие вопросы по тегам