Saya telah membenturkan kepala saya terhadap masalah ini saat menggunakan dan tidak menggunakan aplikasi web yang rumit juga, dan berpikir saya akan menambahkan penjelasan dan solusi saya.
Ketika saya menyebarkan aplikasi di Apache Tomcat, ClassLoader baru dibuat untuk aplikasi itu. ClassLoader kemudian digunakan untuk memuat semua kelas aplikasi, dan saat tidak bekerja, semuanya seharusnya hilang dengan baik. Namun, pada kenyataannya itu tidak sesederhana itu.
Satu atau lebih dari kelas yang dibuat selama kehidupan aplikasi web memiliki referensi statis yang, di suatu tempat sepanjang garis, referensi ClassLoader. Karena referensi awalnya statis, jumlah pengumpulan sampah tidak akan membersihkan referensi ini - ClassLoader, dan semua kelas yang dimuat, ada di sini untuk tinggal.
Dan setelah beberapa penukaran, kami menemukan OutOfMemoryError.
Sekarang ini telah menjadi masalah yang cukup serius. Saya bisa memastikan bahwa Tomcat di-restart setelah setiap pemindahan, tetapi itu menurunkan seluruh server, bukan hanya aplikasi yang dipindah, yang seringkali tidak layak.
Jadi alih-alih saya telah mengumpulkan solusi dalam kode, yang berfungsi pada Apache Tomcat 6.0. Saya belum menguji pada server aplikasi lain, dan harus menekankan bahwa ini sangat mungkin tidak berfungsi tanpa modifikasi pada server aplikasi lain .
Saya juga ingin mengatakan bahwa secara pribadi saya benci kode ini, dan bahwa tidak ada yang harus menggunakan ini sebagai "perbaikan cepat" jika kode yang ada dapat diubah untuk menggunakan metode shutdown dan pembersihan yang tepat . Satu-satunya saat ini harus digunakan adalah jika ada perpustakaan eksternal yang bergantung pada kode Anda (Dalam kasus saya, itu adalah klien RADIUS) yang tidak menyediakan sarana untuk membersihkan referensi statisnya sendiri.
Bagaimanapun, lanjutkan dengan kode. Ini harus disebut pada titik di mana aplikasi tidak dipekerjakan - seperti metode penghancuran servlet atau (pendekatan yang lebih baik) metode konteksDestroyed 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();