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




Вы можете выгрузить ClassLoader, но вы не можете выгружать определенные классы. В частности, вы не можете выгружать классы, созданные в ClassLoader, который не находится под вашим контролем.
Если возможно, я предлагаю использовать ваш собственный ClassLoader, чтобы вы могли выгрузить.
Загрузчики классов могут быть сложной проблемой. Вы можете особенно столкнуться с проблемами, если используете несколько загрузчиков классов и их взаимодействие не определено четко и строго. Я думаю, что для того, чтобы действительно иметь возможность выгрузить класс, который вы собираетесь пойти, необходимо удалить все ссылки на любые классы (и их экземпляры), которые вы пытаетесь выгрузить.
Большинство людей, которым нужно делать такие вещи, в конечном итоге используют OSGi. OSGi действительно мощный, удивительно легкий и простой в использовании,
Единственный способ выгрузки класса - это использование загрузчика классов сборщиком мусора. Это означает, что ссылки на каждый отдельный класс и на сам загрузчик классов должны идти по пути дронта.
Одно из возможных решений вашей проблемы - иметь загрузчик классов для каждого файла jar и загрузчик классов для каждого из серверов приложений, который делегирует фактическую загрузку классов конкретным загрузчикам классов Jar. Таким образом, вы можете указать разные версии файла jar для каждого сервера приложений.
Однако это нетривиально. Платформа OSGi стремится сделать именно это, поскольку каждый пакет имеет свой загрузчик классов, а зависимости решаются платформой. Может быть, хорошим решением будет взглянуть на него.
Если вы не хотите использовать OSGI, одной из возможных реализаций может быть использование одного экземпляра класса JarClassloader для каждого файла JAR.
И создайте новый класс MultiClassloader, расширяющий Classloader. Этот класс внутренне будет иметь массив (или список) загрузчиков JarClass, а в методе defineClass () будет выполняться итерация по всем внутренним загрузчикам классов до тех пор, пока не будет найдено определение или не будет выбрано исключение NoClassDefFoundException. Для добавления новых загрузчиков JarClass в класс может быть предоставлена пара методов доступа. В сети есть несколько возможных реализаций MultiClassLoader, поэтому вам, возможно, даже не потребуется писать свою собственную.
Если вы создаете экземпляр MultiClassloader для каждого подключения к серверу, в принципе возможно, что каждый сервер использует разные версии одного и того же класса.
Я использовал идею MultiClassloader в проекте, где классы, содержащие пользовательские сценарии, должны были загружаться и выгружаться из памяти, и это работало довольно хорошо.
Также обратите внимание, что согласно java.sun.com/docs/books/jls/second_edition/html/… выгрузка классов является оптимизацией и, в зависимости от реализации JVM, может происходить, а может и не происходить.
В качестве более простой и легкой альтернативы OSGi попробуйте Модули JBoss - модульную загрузку классов с загрузчиком классов для каждого модуля (группа банок).
Да, есть способы загрузить классы и «выгружать» их позже. Хитрость заключается в том, чтобы реализовать собственный загрузчик классов, который находится между загрузчиком классов высокого уровня (загрузчик классов System) и загрузчиками классов сервера (ов) приложений, и надеяться, что загрузчики классов сервера приложений делегируют загрузку классов верхним загрузчикам. .
Класс определяется своим пакетом, его именем и загрузчиком классов, который он изначально загружал. Запрограммируйте загрузчик классов «прокси», который загружается первым при запуске JVM. Рабочий процесс:
java.x и sun.x системному загрузчику классов (эти не должен загружаются через любой другой загрузчик классов, кроме системного загрузчика классов).Сделано прямо здесь не должно быть ClassCastException или LinkageError и т. д.
Для получения дополнительной информации об иерархиях загрузчиков классов (да, именно это вы здесь реализуете; -) посмотрите "Серверное программирование на Java", Тед Ньюард - эта книга помогла мне реализовать нечто очень похожее на то, что вы хотите.
Я не понимаю людей, которые отметили -1 для этого ответа, не оставив комментария. На меня смотрится хорошо. Возможно, наличие одного ClassLoader на класс - это слишком много, один ClassLoader на JAR имеет смысл. Можно ли подробнее рассказать о том, как принудительно загрузить класс в предлагаемой схеме? Например. как я могу гарантировать, что экземпляры классов, загруженных ClassLoaderA, не упоминаются экземплярами, загруженными ClassLoaderB?
@dma_k точно, ответ хороший, но он не касается упомянутых вами ключевых моментов.
@Georgi есть ли существующая реализация, которую мы можем создать / повторно использовать для этого?
Было бы очень полезно, если бы вы могли предоставить образец java-кода. Чтобы быть точным, я ищу, как выгружать классы с помощью CustomClassLoader, но мне не повезло.
@ Sriharshag.r.v Вы пробовали это и реализовали образец?
Классы имеют неявную сильную ссылку на свой экземпляр ClassLoader, и наоборот. Они собираются мусором, как и в случае с объектами Java. Вы не можете удалить отдельные классы, не нажав на интерфейс инструментов или аналогичный.
Как всегда возможны утечки памяти. Любая сильная ссылка на один из ваших классов или загрузчик классов приведет к утечке всего этого. Это происходит, например, с реализациями Sun ThreadLocal, java.sql.DriverManager и java.beans.
Я написал собственный загрузчик классов, из которого можно выгружать отдельные классы без сборки загрузчика классов. Загрузчик классов Jar
Работает как шарм :). Есть ли способ выгрузить все файлы классов из файла jar?
К сожалению, пока нет. Но посмотрим на это. Может быть в будущих выпусках.
Кстати, я нашел небольшое обходное решение. Если у вас есть один JarClassLoader для каждого загруженного файла jar, вы можете вызвать для него getLoadedClasses(), затем перебрать каждый и выгрузить его.
Если вы вживую смотрите, работал ли класс выгрузки в JConsole или что-то в этом роде, попробуйте также добавить java.lang.System.gc() в конце логики выгрузки вашего класса. Он явно запускает сборщик мусора.
Осторожно: System.gc () не обязательно вызывать GC. Он только просит jvm запустить его, но не обеспечивает его выполнение. И IME часто не запускает GC: - \
У вас есть загрузчик классов на каждую банку? Как выполняется архивирование контейнеров OSGI для выгрузки загрузчика классов? Похоже, в классе загрузчика классов нет API выгрузки?