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

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

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

Ответы [ 4 ]

59 голосов
/ 19 марта 2009

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

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

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

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

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

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

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

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

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

6 голосов
/ 19 марта 2009

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

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

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

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

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

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

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

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

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

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

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

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

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

EDIT

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

Это JavaRebel от ZeroTurnaround

3 голосов
/ 19 марта 2009

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

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

0 голосов
/ 19 марта 2009

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

...