Как мне "закрыть" ClassLoader?
У меня есть случай, когда мне нужно создать много загрузчиков классов в моем приложении, чтобы временно сделать некоторый код видимым во время работы пользовательских скриптов. Я использую URLClassLoader
для этого и это работает довольно хорошо.
Когда скрипт завершается, я хочу "выгрузить" или "закрыть" загрузчик классов, чтобы освободить ресурсы.
Достаточно ли установить ссылку на загрузчик классов null
? Мне особенно интересно, не закончат ли я в конечном итоге файловые дескрипторы, потому что дополнительные классы находятся в файлах JAR.
PS: должен работать с Java 5 и выше. Да, знаю...
6 ответов
Когда у всех классов, загруженных загрузчиком классов, больше нет ссылок, а все ссылки на сам загрузчик классов удалены, загрузчик классов и загруженные им классы будут собираться как группа.
Обратите внимание, что это зависит от наличия установленного атрибута JVM, который приводит к выгрузке классов, на которые нет ссылок. Он устанавливается по умолчанию в большинстве сред, но может отсутствовать в некоторых встроенных случаях.
[Обратите внимание, что удаление ссылок на класс нетривиально. Любой другой класс, который ссылается на него по имени, конечно, предотвратит удаление. Поэтому класс должен быть загружен с использованием ClassLoader.findClass или чего-то подобного.]
Немного поздно, но, надеюсь, это будет полезно для тех, кто придет к этому вопросу позже (как и я).
В Java 7 метод close() был добавлен в URLClassLoader, что именно то, что запрашивал OP.
РЕДАКТИРОВАТЬ (спасибо @Hot Licks): ОК, так что это не совсем то, о чем просил ОП. Это не освобождает все ресурсы, или делает ресурсы и загрузчик коллекционируемыми. Это просто предотвращает загрузку большего количества ресурсов с помощью загрузчика классов. Однако он закрывает файл JAR, который был загружен с URLClassLoader
,
Если вы не можете использовать Java7 и его метод close(), используйте отражение, чтобы закрыть все открытые JAR-архивы загрузчика классов, например, так:
public void close() {
try {
Class clazz = java.net.URLClassLoader.class;
java.lang.reflect.Field ucp = clazz.getDeclaredField("ucp");
ucp.setAccessible(true);
Object sun_misc_URLClassPath = ucp.get(this);
java.lang.reflect.Field loaders =
sun_misc_URLClassPath.getClass().getDeclaredField("loaders");
loaders.setAccessible(true);
Object java_util_Collection = loaders.get(sun_misc_URLClassPath);
for (Object sun_misc_URLClassPath_JarLoader :
((java.util.Collection) java_util_Collection).toArray()) {
try {
java.lang.reflect.Field loader =
sun_misc_URLClassPath_JarLoader.getClass().getDeclaredField("jar");
loader.setAccessible(true);
Object java_util_jar_JarFile =
loader.get(sun_misc_URLClassPath_JarLoader);
((java.util.jar.JarFile) java_util_jar_JarFile).close();
} catch (Throwable t) {
// if we got this far, this is probably not a JAR loader so skip it
}
}
} catch (Throwable t) {
// probably not a SUN VM
}
return;
}
Если у вас больше нет классов (и объектов), загруженных из этого загрузчика классов, и если вы не сохраняете никаких ссылок на этот загрузчик классов, он будет автоматически обработан сборщиком мусора.
Нет методов close() в загрузчике классов URL или любых его родительских классах, так что вам не повезло.
Разве GC не должен справиться с этим?
Я расширил URLClassLoader и создал метод close, основанный на Java 7s. Я хотел разработать своего IRC бота на своем iPad 2, поэтому я сделал то, что было нужно. Теперь моя система плагинов стабильна на Java 6 и 7, ура.