ฉันได้รับการพิจารณาหัวของฉันกับปัญหานี้ในขณะที่การปรับใช้และยกเลิกการปรับใช้โปรแกรมประยุกต์บนเว็บที่ซับซ้อนด้วยและคิดว่าฉันจะเพิ่มคำอธิบายและการแก้ปัญหาของฉัน
เมื่อฉันปรับใช้แอปพลิเคชันบน Apache Tomcat ClassLoader ใหม่จะถูกสร้างขึ้นสำหรับแอปนั้น จากนั้น ClassLoader จะถูกใช้เพื่อโหลดคลาสทั้งหมดของแอปพลิเคชันและในการเลิกใช้งานทุกอย่างควรจะหายไปอย่างสวยงาม อย่างไรก็ตามในความเป็นจริงมันไม่ง่ายอย่างนั้น
คลาสอย่างน้อยหนึ่งคลาสที่สร้างขึ้นในช่วงชีวิตของเว็บแอปพลิเคชันมีการอ้างอิงแบบคงที่ซึ่งบางแห่งในบรรทัดอ้างอิง ClassLoader เนื่องจากการอ้างอิงนั้นเป็นแบบสแตติกไม่มีการรวบรวมขยะจะล้างข้อมูลอ้างอิงนี้ - ClassLoader และคลาสทั้งหมดที่โหลดอยู่ที่นี่
และหลังจากที่มี redeploys สองสามตัวเราพบ OutOfMemoryError
ตอนนี้ได้กลายเป็นปัญหาร้ายแรง ฉันสามารถตรวจสอบให้แน่ใจว่า Tomcat เริ่มต้นใหม่หลังจากการปรับใช้แต่ละครั้ง แต่จะลงเซิร์ฟเวอร์ทั้งหมดแทนที่จะเป็นเพียงแอปพลิเคชันที่ถูกปรับใช้ซ้ำซึ่งมักจะไม่สามารถทำได้
ดังนั้นฉันจึงรวบรวมวิธีการแก้ปัญหาในรหัสซึ่งทำงานบน Apache Tomcat 6.0 ฉันไม่ได้ทดสอบในเซิร์ฟเวอร์ของโปรแกรมอื่น ๆ และต้องเน้นว่านี้เป็นอย่างมากมีแนวโน้มที่จะไม่ทำงานโดยไม่มีการดัดแปลงบนเซิร์ฟเวอร์แอพลิเคชันอื่น ๆ
ฉันอยากจะบอกด้วยว่าฉันเกลียดรหัสนี้เป็นการส่วนตัวและไม่มีใครควรใช้สิ่งนี้เป็น "การแก้ไขด่วน" หากรหัสที่มีอยู่สามารถเปลี่ยนให้ใช้วิธีการปิดระบบและล้างข้อมูลที่เหมาะสม ครั้งเดียวที่ควรใช้คือหากมีห้องสมุดภายนอกที่รหัสของคุณขึ้นอยู่กับ (ในกรณีของฉันมันเป็นไคลเอนต์ RADIUS) ที่ไม่ได้ให้วิธีการล้างข้อมูลอ้างอิงคงที่ของตัวเอง
อย่างไรก็ตามด้วยรหัส สิ่งนี้ควรถูกเรียก ณ จุดที่แอพพลิเคชั่นไม่ได้ทำการปรับใช้ - เช่นเมธอดการทำลายของ servlet หรือ (วิธีการที่ดีกว่า) เมธอดของบริบทของ ServletContextListener 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();