Я пытался решить эту проблему, развернув и развернув сложное веб-приложение, и решил добавить объяснение и свое решение.
При развертывании приложения в Apache Tomcat для этого приложения создается новый ClassLoader. ClassLoader затем используется для загрузки всех классов приложения, а при отмене развертывания все должно пройти хорошо. Однако на самом деле все не так просто.
Один или несколько классов, созданных в течение жизни веб-приложения, содержат статическую ссылку, которая где-то вдоль строки ссылается на ClassLoader. Поскольку ссылка изначально является статической, никакие сборщики мусора не очистят эту ссылку - ClassLoader и все загруженные классы останутся здесь.
И после нескольких повторных развертываний мы сталкиваемся с ошибкой OutOfMemoryError.
Теперь это стало довольно серьезной проблемой. Я мог бы убедиться в том, что Tomcat перезапускается после каждого повторного развертывания, но это приводит к отключению всего сервера, а не просто повторного развертывания приложения, что часто неосуществимо.
Так что вместо этого я собрал решение в коде, которое работает на Apache Tomcat 6.0. Я не тестировал ни на каких других серверах приложений и должен подчеркнуть, что очень вероятно, что не будет работать без изменений на любом другом сервере приложений .
Я также хотел бы сказать, что лично я ненавижу этот код, и что никто не должен использовать это как "быстрое исправление", если существующий код можно изменить для использования надлежащих методов выключения и очистки, Единственный раз, когда это следует использовать, это если есть внешняя библиотека, от которой зависит ваш код (в моем случае это был клиент RADIUS), который не предоставляет средства для очистки своих собственных статических ссылок.
В любом случае, с кодом. Это следует вызывать в тот момент, когда приложение развертывается - например, метод уничтожения сервлета или (лучший подход) метод contextDestroyed ServletContextListener.
//Get a list of all classes loaded by the current webapp classloader
WebappClassLoader classLoader = (WebappClassLoader) getClass().getClassLoader();
Field classLoaderClassesField = null;
Class clazz = WebappClassLoader.class;
while (classLoaderClassesField == null && clazz != null) {
try {
classLoaderClassesField = clazz.getDeclaredField("classes");
} catch (Exception exception) {
//do nothing
}
clazz = clazz.getSuperclass();
}
classLoaderClassesField.setAccessible(true);
List classes = new ArrayList((Vector)classLoaderClassesField.get(classLoader));
for (Object o : classes) {
Class c = (Class)o;
//Make sure you identify only the packages that are holding references to the classloader.
//Allowing this code to clear all static references will result in all sorts
//of horrible things (like java segfaulting).
if (c.getName().startsWith("com.whatever")) {
//Kill any static references within all these classes.
for (Field f : c.getDeclaredFields()) {
if (Modifier.isStatic(f.getModifiers())
&& !Modifier.isFinal(f.getModifiers())
&& !f.getType().isPrimitive()) {
try {
f.setAccessible(true);
f.set(null, null);
} catch (Exception exception) {
//Log the exception
}
}
}
}
}
classes.clear();