Tôi đã cố gắng chống lại vấn đề này trong khi triển khai và không triển khai một ứng dụng web phức tạp, và nghĩ rằng tôi sẽ thêm một lời giải thích và giải pháp của mình.
Khi tôi triển khai một ứng dụng trên Apache Tomcat, ClassLoader mới được tạo cho ứng dụng đó. ClassLoader sau đó được sử dụng để tải tất cả các lớp của ứng dụng và khi không triển khai, mọi thứ sẽ biến mất một cách độc đáo. Tuy nhiên, trong thực tế nó không hoàn toàn đơn giản.
Một hoặc nhiều lớp được tạo trong vòng đời của ứng dụng web chứa một tham chiếu tĩnh, ở đâu đó dọc theo dòng, tham chiếu ClassLoader. Vì tham chiếu ban đầu là tĩnh, nên không có lượng thu gom rác nào sẽ dọn sạch tham chiếu này - Trình tải lớp và tất cả các lớp được tải, sẽ ở đây.
Và sau một vài lần tái cấu trúc, chúng ta bắt gặp OutOfMemoryError.
Bây giờ điều này đã trở thành một vấn đề khá nghiêm trọng. Tôi có thể chắc chắn rằng Tomcat được khởi động lại sau mỗi lần triển khai lại, nhưng điều đó sẽ phá hủy toàn bộ máy chủ, thay vì chỉ ứng dụng được triển khai lại, điều này thường không khả thi.
Vì vậy, thay vào đó tôi đã đưa ra một giải pháp trong mã, hoạt động trên Apache Tomcat 6.0. Tôi chưa thử nghiệm trên bất kỳ máy chủ ứng dụng nào khác và phải nhấn mạnh rằng điều này rất có thể không hoạt động nếu không sửa đổi trên bất kỳ máy chủ ứng dụng nào khác .
Tôi cũng muốn nói rằng cá nhân tôi ghét mã này và không ai nên sử dụng nó như một "sửa chữa nhanh" nếu mã hiện tại có thể được thay đổi để sử dụng các phương pháp tắt và dọn dẹp hợp lý . Lần duy nhất nên sử dụng là nếu có thư viện bên ngoài mà mã của bạn phụ thuộc vào (Trong trường hợp của tôi, đó là máy khách RADIUS) không cung cấp phương tiện để dọn sạch các tham chiếu tĩnh của chính nó.
Dù sao, với mã. Điều này nên được gọi tại thời điểm ứng dụng không triển khai - chẳng hạn như phương thức hủy của một servlet hoặc (cách tiếp cận tốt hơn) một phương thức contextDestroyed của 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();