Я також мав голову проти цієї проблеми під час розгортання та нерозгортання складного веб-додатка, і думав, що я додам пояснення та своє рішення.
Коли я розгортаю програму на Apache Tomcat, для цього додатка створюється новий ClassLoader. Потім ClassLoader використовується для завантаження всіх класів програми, а в режимі неелектричної роботи все повинно пройти непогано. Однак насправді це не так просто.
Один або кілька класів, створених протягом життя веб-програми, містить статичну довідку, яка десь уздовж лінії посилається на ClassLoader. Оскільки посилання спочатку статична, жодна кількість збирання сміття не очистить цю довідку - ClassLoader та всі класи, які вона завантажує, залишаються тут.
І після пари повторних розборок ми стикаємося з OutOfMemoryError.
Зараз це стало досить серйозною проблемою. Я міг би переконатися, що Tomcat перезапускається після кожного повторного розгортання, але це забирає весь сервер, а не лише перепрограмування програми, що часто неможливо.
Тому замість цього я зібрав рішення в коді, яке працює на Apache Tomcat 6.0. Я не тестувався на будь-яких інших серверах прикладних програм, і потрібно наголосити, що це, швидше за все, не працює без змін на будь-якому іншому сервері додатків .
Я також хотів би сказати, що особисто я ненавиджу цей код, і що ніхто не повинен використовувати це як "швидке виправлення", якщо існуючий код можна змінити для використання правильних методів відключення та очищення . Єдиний раз, коли це слід використати, це якщо є зовнішня бібліотека, від якої залежить ваш код (у моєму випадку це був клієнт RADIUS), який не забезпечує засоби для очищення власних статичних посилань.
У всякому разі, з кодом. Це слід викликати у тому випадку, коли додаток не працює, наприклад, метод знищення сервлета або (кращий підхід) метод контекстування Devstroy 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();