Как мне "закрыть" 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, ура.

Другие вопросы по тегам