Também tenho enfrentado esse problema ao implantar e desimplementar um aplicativo Web complexo, e pensei em adicionar uma explicação e minha solução.
Quando implanto um aplicativo no Apache Tomcat, um novo ClassLoader é criado para esse aplicativo. O ClassLoader é usado para carregar todas as classes do aplicativo e, na desimplantação, tudo deve desaparecer bem. No entanto, na realidade, não é tão simples.
Uma ou mais das classes criadas durante a vida útil do aplicativo Web mantém uma referência estática que, em algum ponto da linha, faz referência ao ClassLoader. Como a referência é originalmente estática, nenhuma quantidade de coleta de lixo limpará essa referência - o ClassLoader e todas as classes carregadas vieram para ficar.
Depois de algumas reimplantações, encontramos o OutOfMemoryError.
Agora isso se tornou um problema bastante sério. Eu poderia garantir que o Tomcat fosse reiniciado após cada reimplantação, mas isso derrubaria todo o servidor, em vez de apenas o aplicativo que está sendo reimplantado, o que geralmente não é possível.
Então, em vez disso, montei uma solução em código, que funciona no Apache Tomcat 6.0. Não testei em nenhum outro servidor de aplicativos e devo enfatizar que é muito provável que isso não funcione sem modificação em nenhum outro servidor de aplicativos .
Eu também gostaria de dizer que pessoalmente odeio esse código e que ninguém deve usá-lo como uma "solução rápida" se o código existente puder ser alterado para usar métodos adequados de desligamento e limpeza . A única vez que isso deve ser usado é se houver uma biblioteca externa da qual seu código dependa (no meu caso, era um cliente RADIUS) que não fornece um meio de limpar suas próprias referências estáticas.
Enfim, continue com o código. Isso deve ser chamado no ponto em que o aplicativo não está sendo implementado - como o método de destruição de um servlet ou (a melhor abordagem) o método contextDestroyed de um 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();