Ben de karmaşık bir web uygulaması dağıtırken ve çözerken bu soruna karşı başımı eğiyordum ve bir açıklama ve çözüm ekleyeceğimi düşündüm.
Bir uygulamayı Apache Tomcat'e dağıttığımda, o uygulama için yeni bir ClassLoader oluşturulur. Daha sonra ClassLoader, uygulamanın tüm sınıflarını yüklemek için kullanılır ve çözüldüğünde, her şeyin güzelce gitmesi gerekir. Ancak, gerçekte o kadar basit değil.
Web uygulamasının ömrü boyunca oluşturulan bir veya daha fazla sınıf, çizgi boyunca bir yerde ClassLoader'a başvuran statik bir başvuru içerir. Referans başlangıçta statik olduğundan, hiçbir çöp toplama işlemi bu referansı temizlemez - ClassLoader ve yüklenen tüm sınıflar burada kalır.
Ve birkaç tekrardan sonra OutOfMemoryError ile karşılaşıyoruz.
Şimdi bu oldukça ciddi bir sorun haline geldi. Tomcat'in her yeniden konuşlandırmadan sonra yeniden başlatıldığından emin olabilirim, ancak bu, uygulamanın yeniden dağıtılmasından ziyade sunucunun tamamını kapar ve bu genellikle mümkün değildir.
Bunun yerine Apache Tomcat 6.0 üzerinde çalışan kodda bir çözüm oluşturdum. Başka bir uygulama sunucusunda test etmedim ve bunun başka bir uygulama sunucusunda değişiklik yapmadan çalışmamasının çok olası olduğunu vurgulamalıyım .
Ben de kişisel olarak bu kod nefret ve mevcut kod uygun kapatma ve temizleme yöntemleri kullanmak için değiştirilebilir eğer kimse bu "hızlı düzeltme" olarak kullanmak gerektiğini söylemek istiyorum . Kullanılması gereken tek zaman, kodunuzun bağımlı olduğu harici bir kütüphane varsa (benim durumumda, bir RADIUS istemcisiydi) kendi statik referanslarını temizlemek için bir yol sağlamaz.
Her neyse, kodla devam et. Bu, uygulamanın bir sunucu uygulamasının destroy yöntemi veya (daha iyi bir yaklaşım) ServletContextListener'ın contextDestroyed yöntemi gibi uygulamanın çözülmediği noktada çağrılmalıdır.
//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();