Разгрузка классов в Java? - PullRequest
       68

Разгрузка классов в Java?

164 голосов
/ 29 сентября 2008

У меня есть пользовательский загрузчик классов, чтобы приложение для настольных компьютеров могло динамически начинать загрузку классов с сервера приложений, с которым мне нужно поговорить. Мы сделали это, так как количество банок, необходимых для этого, просто смешно (если мы хотим отправить их). У нас также возникают проблемы с версиями, если мы не загружаем классы динамически во время выполнения из библиотеки AppServer.

Теперь я просто столкнулся с проблемой, когда мне нужно поговорить с двумя разными AppServer и обнаружил, что в зависимости от того, чьи классы я загружаю первым, я могу плохо сломаться ... Есть ли способ принудительно выгрузить класс, не убивая JVM?

Надеюсь, это имеет смысл

Ответы [ 7 ]

180 голосов
/ 29 сентября 2008

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

Одним из возможных решений вашей проблемы является наличие Classloader для каждого jar-файла и Classloader для каждого из AppServers, который делегирует фактическую загрузку классов определенным загрузчикам классов Jar. Таким образом, вы можете указать разные версии файла JAR для каждого сервера приложений.

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

Если вы не хотите использовать OSGI, одной из возможных реализаций может быть использование одного экземпляра класса JarClassloader для каждого файла JAR.

И создайте новый класс MultiClassloader, который расширяет Classloader. Этот класс внутренне будет иметь массив (или List) JarClassloaders, а в методе defineClass () будет перебирать все внутренние загрузчики классов до тех пор, пока не будет найдено определение или не будет сгенерировано исключение NoClassDefFoundException. Для добавления новых классов JarClassloaders в класс может быть предоставлена ​​пара методов доступа. Существует несколько возможных реализаций в сети для MultiClassLoader, поэтому вам даже не нужно писать свой собственный.

Если вы создаете экземпляр MultiClassloader для каждого соединения с сервером, в принципе возможно, что каждый сервер использует разные версии одного и того же класса.

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

38 голосов
/ 29 сентября 2008

Да, есть способы загружать классы и «выгружать» их позже. Хитрость заключается в том, чтобы реализовать свой собственный загрузчик классов, который находится между загрузчиком классов высокого уровня (загрузчиком классов System) и загрузчиками классов серверов приложений, и надеяться, что загрузчики классов сервера приложений действительно делегируют загрузку классов верхним загрузчикам. .

Класс определяется его пакетом, его именем и загрузчиком классов, который он первоначально загрузил. Запрограммируйте загрузчик классов «прокси», который загружается первым при запуске JVM. Процедура:

  • Программа запускается и настоящий «главный» класс загружается этим прокси-загрузчиком классов.
  • Каждый класс, который затем обычно загружается (т.е. не через другую реализацию загрузчика классов, которая может нарушить иерархию), будет делегирован этому загрузчику классов.
  • Прокси-загрузчик классов делегирует java.x и sun.x системному загрузчику классов (эти не должны загружаться через любой другой загрузчик классов, кроме системного загрузчика классов).
  • Для каждого заменяемого класса создайте экземпляр загрузчика классов (который действительно загружает класс и не делегирует его родительскому загрузчику классов) и загрузите его через него.
  • Сохранить пакет / имя классов в качестве ключей, а загрузчик классов - в качестве значений в структуре данных (т. Е. Hashmap).
  • Каждый раз, когда прокси-загрузчик классов получает запрос на класс, который был загружен ранее, он возвращает класс из загрузчика классов, сохраненного ранее.
  • Этого должно быть достаточно, чтобы найти байтовый массив класса вашим загрузчиком классов (или «удалить» пару ключ / значение из вашей структуры данных) и перезагрузить класс в случае, если вы хотите изменить его.

Готово, не должно быть ClassCastException или LinkageError и т. Д.

Для получения дополнительной информации об иерархиях загрузчиков классов (да, это именно то, что вы реализуете здесь; -) посмотрите «Программирование на сервере Java» Теда Ньюарда - эта книга помогла мне реализовать нечто очень похожее к тому, что вы хотите.

14 голосов
/ 06 июля 2010

Я написал собственный загрузчик классов, из которого можно выгружать отдельные классы без GCing загрузчика классов. Jar Class Loader

12 голосов
/ 29 сентября 2008

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

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

6 голосов
/ 29 сентября 2008

Вы можете выгрузить ClassLoader, но вы не можете выгрузить определенные классы. Более конкретно, вы не можете выгружать классы, созданные в ClassLoader, который не находится под вашим контролем.

Если возможно, я предлагаю использовать ваш собственный ClassLoader, чтобы вы могли выгрузить.

4 голосов
/ 29 сентября 2008

Классы имеют неявную строгую ссылку на свой экземпляр ClassLoader, и наоборот. Они мусор, как с объектами Java. Без использования интерфейса инструментов или подобного вам нельзя удалить отдельные классы.

Как всегда, вы можете получить утечки памяти. Любая сильная ссылка на один из ваших классов или загрузчик классов утечет все это. Это происходит с реализациями Sun ThreadLocal, например, java.sql.DriverManager и java.beans.

0 голосов
/ 15 октября 2017

Если вы смотрите в режиме реального времени, работает ли класс выгрузки в JConsole или что-то еще, попробуйте добавить java.lang.System.gc() в конце логики выгрузки класса. Он явно запускает сборщик мусора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...