मैं एक जटिल वेब एप्लिकेशन को भी तैनात और तैयार नहीं करते हुए इस समस्या के खिलाफ अपना सिर झुका रहा हूं, और मैंने सोचा कि मैं एक स्पष्टीकरण और मेरा समाधान जोड़ूंगा।
जब मैं Apache Tomcat पर एक एप्लिकेशन को तैनात करता हूं, तो उस ऐप के लिए एक नया ClassLoader बनाया जाता है। तब ClassLoader का उपयोग सभी एप्लिकेशन की कक्षाओं को लोड करने के लिए किया जाता है, और undeploy पर, सब कुछ अच्छी तरह से दूर जाना है। हालांकि, वास्तव में यह उतना सरल नहीं है।
वेब एप्लिकेशन के जीवन के दौरान बनाई गई एक या अधिक कक्षाएं एक स्थिर संदर्भ रखती हैं, जो कहीं-कहीं लाइन के साथ, क्लासऑलाडर का संदर्भ देती है। जैसा कि संदर्भ मूल रूप से स्थिर है, कचरा संग्रह की कोई भी राशि इस संदर्भ को साफ नहीं करेगी - क्लासॉलाडर, और सभी वर्ग जो इसे लोड किए गए हैं, यहां रहने के लिए हैं।
और redeploys के एक जोड़े के बाद, हम OutOfMemoryError से मुठभेड़ करते हैं।
अब यह काफी गंभीर समस्या बन गई है। मैं यह सुनिश्चित कर सकता था कि प्रत्येक redeploy के बाद Tomcat को फिर से शुरू किया जाए, लेकिन यह पूरे सर्वर को नीचे ले जाता है, बजाय इसके कि केवल एप्लिकेशन को redeployed किया जाए, जो अक्सर संभव नहीं होता है।
इसलिए इसके बजाय मैंने कोड में एक समाधान डाला है, जो Apache Tomcat 6.0 पर काम करता है। मैंने किसी अन्य एप्लिकेशन सर्वर पर परीक्षण नहीं किया है, और इस बात पर जोर देना चाहिए कि यह बहुत संभव है कि किसी अन्य एप्लिकेशन सर्वर पर संशोधन के बिना काम न करे ।
मैं यह भी कहना चाहूंगा कि व्यक्तिगत रूप से मुझे इस कोड से नफरत है, और किसी को भी इसे "क्विक फिक्स" के रूप में उपयोग नहीं करना चाहिए यदि मौजूदा कोड को उचित शटडाउन और सफाई विधियों का उपयोग करने के लिए बदला जा सकता है । इसका उपयोग केवल उसी समय किया जाना चाहिए जब कोई बाहरी लाइब्रेरी आपके कोड पर निर्भर हो (मेरे मामले में, यह एक RADIUS क्लाइंट था) जो अपने स्वयं के स्थैतिक संदर्भों को साफ करने का साधन प्रदान नहीं करता है।
वैसे भी, कोड के साथ। इसे उस बिंदु पर बुलाया जाना चाहिए, जहां एप्लिकेशन undeploying है - जैसे कि सर्वलेट की विध्वंस विधि या (बेहतर दृष्टिकोण) एक सर्वलेटकोटेक्स्टलेनर के संदर्भडिस्ट्रोइड विधि।
//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();