Odwracam głowę od tego problemu podczas wdrażania i cofania również złożonej aplikacji internetowej i pomyślałem, że dodam wyjaśnienie i moje rozwiązanie.
Kiedy wdrażam aplikację na Apache Tomcat, tworzony jest nowy ClassLoader dla tej aplikacji. ClassLoader jest następnie używany do ładowania wszystkich klas aplikacji, a po cofnięciu wszystko powinno ładnie zniknąć. Jednak w rzeczywistości nie jest to takie proste.
Jedna lub więcej klas utworzonych podczas życia aplikacji internetowej zawiera odwołanie statyczne, które gdzieś wzdłuż linii odwołuje się do ClassLoadera. Ponieważ referencja jest pierwotnie statyczna, żadna ilość czyszczenia pamięci nie wyczyści tej referencji - ClassLoader i wszystkie klasy, które są ładowane, pozostaną.
Po kilku ponownych wdrożeniach napotykamy błąd OutOfMemoryError.
Teraz stało się to dość poważnym problemem. Mogłem się upewnić, że Tomcat zostanie zrestartowany po każdym ponownym wdrożeniu, ale spowoduje to usunięcie całego serwera, a nie tylko aplikacji, co często nie jest możliwe.
Zamiast tego stworzyłem rozwiązanie w kodzie, które działa na Apache Tomcat 6.0. Nie testowałem na żadnym innym serwerze aplikacji i muszę podkreślić, że prawdopodobnie nie zadziała to bez modyfikacji na żadnym innym serwerze aplikacji .
Chciałbym również powiedzieć, że osobiście nienawidzę tego kodu, i że nikt nie powinien używać go jako „szybkiej poprawki”, jeśli istniejący kod można zmienić w celu użycia odpowiednich metod zamykania i czyszczenia . Należy go użyć tylko wtedy, gdy istnieje biblioteka zewnętrzna, od której zależy Twój kod (w moim przypadku był to klient RADIUS), który nie zapewnia możliwości czyszczenia własnych odniesień statycznych.
W każdym razie, dalej z kodem. Powinno to zostać wywołane w miejscu, w którym aplikacja się wdraża - na przykład metoda zniszczenia serwletu lub (lepsze podejście) metoda kontekstDestroyed 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();