Утечка памяти метапространства: пользовательский загрузчик классов не может быть gc из-за того, что inheritedAccessContrJAVA

Программисты JAVA общаются здесь
Ответить Пред. темаСлед. тема
Гость
 Утечка памяти метапространства: пользовательский загрузчик классов не может быть gc из-за того, что inheritedAccessContr

Сообщение Гость »


Мой код написан на jdk8. Я использую собственный загрузчик классов для горячего обновления классов (назовем классы сценариев, они находятся в пакете com.xxx.project1.script), помеченных специальной аннотацией @Script. :

@Retention(RetentionPolicy.RUNTIME) @Target({ТипЭлемента.ТИП}) общедоступный сценарий @interface { int order() по умолчанию 0; } Мой собственный загрузчик классов:

публичный класс ScriptClassLoader расширяет URLClassLoader { частный String classPackage; частный ClassLoader defaultClassLoader; частная логическая функция dev; public static ScriptClassLoader newInstance(URL[] urls, String classPackage, boolean dev) { если (dev) { urls = ((URLClassLoader)ScriptClassLoader.class.getClassLoader()).getURLs(); } вернуть новый ScriptClassLoader(urls, classPackage, dev); } protected ScriptClassLoader(URL[] urls, String classPackage, boolean dev) { супер (URL-адреса); this.classPackage = classPackage; this.dev = Дев; this.defaultClassLoader = ScriptClassLoader.class.getClassLoader(); } protected Class loadClass(имя строки, логическое разрешение) выдает ClassNotFoundException { if (!this.dev && name.startsWith(this.classPackage)) { Class c = this.findLoadedClass(name); если (с == ноль) { c = this.findClass(имя); } если (решить) { this.resolveClass(c); } вернуть с; } еще { вернуть super.loadClass(имя, разрешение); } } } Недавно я обнаружил, что после перезагрузки классов сценариев исходные классы сценариев не выгружались. Причина в том, что при создании нового потока он будет содержать новый объект контекста (см. java.lang.Thread):

this.inheritedAccessControlContext = акк!= ноль? Acc: AccessController.getContext(); Итак, если класс сценария создается таким потоком, объект будет содержать объект ScriptClassLoader, используемый для загрузки класса сценария, из-за чего этот ScriptClassLoader не может быть GC и, следовательно, не может выгружать бесполезные классы сценариев.

com.xxx.script.ScriptClassLoader -classloader java.security.ProtectionDomain --[4] java.security.ProtectionDomain[7] ---context java.security.AccessControlContext ----inheritedAccessControlContext java.lang.Thread Поскольку мне неясно, как использовать inheritedAccessControlContext и к нему нельзя получить доступ из внешнего класса, текущее решение состоит в том, чтобы инициализировать все основные потоки при создании пула потоков, избегая создания потоков, инициируемого Классы сценариев:

public static ScheduledExecutorService newScheduledThreadPool(int coreSize, String namePrefix) { Служба ScheduledExecutorService = Executors.newScheduledThreadPool(coreSize, new ThreadFactory() { окончательный AtomicInteger n = новый AtomicInteger(); @Override общественный поток newThread (Runnable r) { Имя строки = namePrefix + "-" + n.incrementAndGet()); вернуть новый поток (r, имя); } }); ((ScheduledThreadPoolExecutor) сервис).prestartAllCoreThreads(); // инициализируем все основные потоки услуга возврата; } Но есть еще некоторые скрытые опасности. Потому что Java загружает классы только тогда, когда они используются. Поэтому в некоторых местах, даже если записаны статические константы, классы сценариев все равно могут инициировать создание, например:

абстрактный публичный класс Http { частный статический окончательный ScheduledExecutorService EXECUTOR = Executors.newScheduledThreadPool(16, новый ThreadFactory() { частный окончательный счетчик AtomicInteger = новый AtomicInteger(0); @Override общественный поток newThread (Runnable r) { return new Thread(r, "http-request-" + counter.incrementAndGet()); } } ); Если вы впервые используете этот класс в классе сценария, то он, несомненно, вернется к описанной выше проблеме. Текущее решение состоит в том, чтобы заранее вызывать эти классы/методы вне сценария во время запуска программы, чтобы инициировать их инициализацию, например:

private static void envirInit() { HTTP.init(); DBUpdateUtil.init(); } Это кажется глупым. Есть ли лучшее решение?
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ, комментарий, отзыв

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «JAVA»