2009-03-18 2 views
43

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

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

+0

Почему это сообщество Wiki, кстати? – Eddie

+2

Думал, что это может помочь в случае, если я оставил что-то непонятное ... сделайте это по умолчанию, это плохой этикет? –

+0

Забавный: нашел, подумал-о, перечитывая сейчас, спасибо! –

ответ

58

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

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

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

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

Вы можете иногда исправить это:

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

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

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

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

+1

Эдди - не причина для сохранения классов, потому что уже существуют объекты, которые стучат вокруг с «старым» определением класса? –

+0

@Edie: Большинство ссылок связаны с памятью, а не с архитектурой classloader, что было бы наиболее полезно понять, почему проблема с hot-deploy является проблемой. Проблема с памятью является следствием обходных решений. – OscarRyz

+0

Просто заметка о запуске потоков: когда я покинул поток, чтобы спать по поводу повторного развертывания по ошибке, момент, когда поток проснулся, бросил NoClassDefFound (классы были нераспределены), а затем (конечно) умер. После его смерти PermGen должен быть ОК, чтобы быть GCed. –

6

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

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

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

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

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

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

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

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

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

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

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

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

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

EDIT

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

Это JavaRebel from ZeroTurnaround

+0

какой беспорядок в твоей голове, а? поэтому java не был предназначен для загрузки классов во время выполнения, а? поэтому он имеет Class.forName() с самого начала? –

+0

Это не то, что Оскар написал Владимиру. Он писал, что Java не предназначена для замены классов во время выполнения. –

+0

Класс ClassLoader существует с версии 1. Не предназначен для замены классов во время выполнения? Кроме того, безопасность не имеет ничего общего с PermMen OOM. Предел PermGen - всего лишь неприятная деталь реализации Sun JVM. –

3

Sun JVM имеет PermGen неподвижное пространство, и в конечном итоге все это потребляется (да, по-видимому, из-за ошибки в загрузчика классов, связанных с кодом) => ООМ.

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

+0

Подождите. Это не соответствует действительности на 100%. Проблема perm gem связана не только с JVM и его библиотеками. Плохое приложение также может вызывать ошибки PermGen. Кроме того, динамическое расширение PerGen абсолютно не отличается от его решения. Подождите достаточно времени в любой виртуальной машине и проблема будет. – Antonio

+1

Исправить. Тем не менее, динамический PermGen позволяет вам пережить время более горячих развертываний. В Sun JVM это часто происходит при перераспределении # 1: -E –

0

Какую версию java вы используете? Были ошибки в раннем Sun 1.4.2, но он работает долгое время.
BTW, как вы можете поделиться новостями с командой? Вы руководите командой?

+0

На самом деле это новая проблема с 1,5. Или, по крайней мере, я столкнулся с ним только в 1,5. –

+0

Мы столкнулись с этой проблемой с 1.6. Я, вероятно, «сломаю это» в моей команде, отправив ему ссылку на эту страницу :-P –

Смежные вопросы