Я бился головой об этой проблеме, когда развертывал и развертывал сложные веб-приложения, и думал, что добавлю объяснение и свое решение.
Когда я развертываю приложение в 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();