ยกเลิกการโหลดคลาสใน java?


174

ฉันมีตัวโหลดคลาสที่กำหนดเองเพื่อให้แอปพลิเคชันเดสก์ท็อปสามารถเริ่มต้นการโหลดคลาสจาก AppServer ที่ฉันต้องคุยด้วย เราทำเช่นนี้เนื่องจากจำนวนขวดที่ต้องใช้ในการทำเช่นนี้ถือว่าไร้สาระ (ถ้าเราต้องการจัดส่ง) เรายังมีปัญหารุ่นหากเราไม่โหลดคลาสแบบไดนามิก ณ รันไทม์จากไลบรารี AppServer

ตอนนี้ฉันเพิ่งพบปัญหาที่ฉันต้องพูดคุยกับ AppServers สองคนที่แตกต่างกันและพบว่าขึ้นอยู่กับว่าฉันโหลดคลาสใดก่อนใครจะแตกหัก ...

หวังว่ามันจะสมเหตุสมผล


คุณมี classloader สำหรับแต่ละ jar หรือไม่? OSGI คอนเทนเนอร์เก็บถาวรเพื่อยกเลิกการโหลด classloader อย่างไร ดูเหมือนว่าไม่มีการยกเลิกการโหลด API ในคลาส classloader?
hetaoblog

คำตอบ:


190

วิธีเดียวที่ Class สามารถยกเลิกการโหลดคือถ้า Classloader ใช้เป็นขยะที่รวบรวม ซึ่งหมายความว่าการอ้างอิงถึงทุก ๆ คลาสและตัวโหลดคลาสเองต้องไปตามทางของโดโด

ทางออกหนึ่งที่เป็นไปได้สำหรับปัญหาของคุณคือมี Classloader สำหรับไฟล์ jar ทุกไฟล์และ Classloader สำหรับ AppServers แต่ละตัวที่มอบหมายการโหลดคลาสจริงให้กับ Classloaders Jar เฉพาะ ด้วยวิธีนี้คุณสามารถชี้ไปที่ไฟล์ jar เวอร์ชันต่างๆสำหรับทุกเซิร์ฟเวอร์แอป

แม้ว่ามันจะไม่สำคัญก็ตาม แพลตฟอร์ม OSGi พยายามทำเช่นนี้เนื่องจากแต่ละบันเดิลมี classloader ที่แตกต่างกันและการพึ่งพาได้รับการแก้ไขโดยแพลตฟอร์ม บางทีทางออกที่ดีก็คือดูที่มัน

หากคุณไม่ต้องการใช้ OSGI การใช้งานที่เป็นไปได้หนึ่งอย่างคือการใช้JarClassloaderคลาสหนึ่งอินสแตนซ์สำหรับไฟล์ JAR ทุกไฟล์

และสร้างคลาส MultiClassloader ใหม่ที่ขยาย Classloader คลาสนี้ภายในจะมีอาร์เรย์ (หรือรายการ) ของ JarClassloaders และในเมธอด defineClass () จะวนซ้ำผ่าน classloaders ภายในทั้งหมดจนกว่าจะสามารถหาคำจำกัดความได้หรือ NoClassDefFoundException ถูกโยนทิ้ง สามารถให้เมธอด accessor สองวิธีเพื่อเพิ่ม JarClassloaders ใหม่ให้กับคลาส มีการใช้งานที่เป็นไปได้หลายอย่างในเน็ตสำหรับ MultiClassLoader ดังนั้นคุณอาจไม่จำเป็นต้องเขียนด้วยตัวเอง

หากคุณทำการติดตั้ง MultiClassloader สำหรับทุกการเชื่อมต่อกับเซิร์ฟเวอร์โดยหลักการแล้วเป็นไปได้ว่าเซิร์ฟเวอร์ทุกเครื่องจะใช้คลาสเดียวกันในเวอร์ชันที่แตกต่างกัน

ฉันใช้แนวคิด MultiClassloader ในโครงการที่มีคลาสที่มีสคริปต์ที่ผู้ใช้กำหนดเองต้องโหลดและยกเลิกการโหลดจากหน่วยความจำและทำงานได้ค่อนข้างดี


31
โปรดทราบว่าตามjava.sun.com/docs/books/jls/second_edition/html/…การยกเลิกการโหลดคลาสเป็นการเพิ่มประสิทธิภาพและขึ้นอยู่กับการใช้งาน JVM อาจหรืออาจไม่เกิดขึ้นจริง

5
ในฐานะที่เป็นทางเลือกที่ง่ายและมีน้ำหนักเบาสำหรับ OSGi ให้ลองใช้โมดูล JBoss - การโหลดคลาสแบบโมดูลาร์ด้วย classloader ต่อโมดูล (กลุ่มขวด)
Ondra Žižka

42

ใช่มีวิธีในการโหลดคลาสและ "ยกเลิกการโหลด" ในภายหลัง เคล็ดลับคือการใช้ classloader ของคุณเองซึ่งอยู่ระหว่างตัวโหลดคลาสระดับสูง (System class loader) และตัวโหลดคลาสของเซิร์ฟเวอร์แอปและหวังว่าคลาสตัวโหลดเซิร์ฟเวอร์ของแอปจะมอบหมายการโหลดคลาสไปยังตัวโหลดส่วนบน .

คลาสถูกกำหนดโดยแพ็กเกจชื่อและคลาสโหลดมันที่โหลดไว้ในตอนแรก โปรแกรมตัวโหลดคลาส "พร็อกซี" ซึ่งเป็นตัวแรกที่โหลดเมื่อเริ่มต้น JVM ขั้นตอนการทำงาน:

  • โปรแกรมเริ่มทำงานและโหลดคลาสหลัก "หลัก" จริงโดย classloader พร็อกซีนี้
  • ทุกคลาสที่โหลดตามปกติแล้ว (เช่นไม่ผ่านการใช้งานตัวโหลดคลาสอื่นซึ่งอาจทำลายลำดับชั้น) จะถูกมอบให้กับคลาสโหลดเดอร์นี้
  • พร็อกซี classloader มอบหมายjava.xและsun.xระบบ classloader (เหล่านี้จะต้องไม่โหลดผ่าน classloader อื่น ๆ กว่าระบบ classloader)
  • สำหรับทุกคลาสที่สามารถเปลี่ยนได้ให้สร้างอินสแตนซ์ของ classloader (ซึ่งโหลดคลาสจริงๆและไม่มอบให้กับ parent classloader) และโหลดผ่านสิ่งนี้
  • เก็บแพ็กเกจ / ชื่อคลาสเป็นคีย์และ classloader เป็นค่าในโครงสร้างข้อมูล (เช่น Hashmap)
  • ทุกครั้งที่ proxy classer พร็อกซีได้รับการร้องขอสำหรับคลาสที่โหลดมาก่อนจะส่งคืนคลาสจากคลาสโหลดเดอร์ที่เก็บไว้ก่อนหน้านี้
  • มันควรจะเพียงพอที่จะค้นหาอาร์เรย์ไบต์ของคลาสโดยตัวโหลดคลาสของคุณ (หรือเพื่อ "ลบ" คู่คีย์ / ค่าจากโครงสร้างข้อมูลของคุณ) และโหลดคลาสใหม่ในกรณีที่คุณต้องการเปลี่ยน

ทำถูกต้องไม่ควรมีClassCastExceptionหรือLinkageErrorเป็นต้น

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับลำดับชั้นของคลาสโหลดเดอร์ (ใช่นั่นคือสิ่งที่คุณกำลังนำมาใช้ที่นี่ -) ดูที่"การเขียนโปรแกรม Java บนเซิร์ฟเวอร์" โดย Ted Neward - หนังสือเล่มนั้นช่วยให้ฉันสามารถนำสิ่งที่คล้ายกับที่คุณต้องการ


3
ฉันไม่เข้าใจคนที่ทำเครื่องหมายเลือก -1 สำหรับคำตอบนี้โดยไม่แสดงความคิดเห็น สำหรับฉันดูดี บางทีการมีหนึ่งClassLoaderคลาสต่อหนึ่งอาจมากเกินไปหนึ่งรายการClassLoaderต่อ JAR จะสมเหตุสมผล มีความเฉพาะเจาะจงมากขึ้นเกี่ยวกับวิธีบังคับการอัปโหลดคลาสในสคีมาที่เสนอหรือไม่ เช่นฉันจะรับประกันได้อย่างไรว่าอินสแตนซ์ของคลาสที่โหลดโดย ClassLoaderA ไม่ได้ถูกอ้างอิงโดยอินสแตนซ์ที่โหลดโดย ClassLoaderB
dma_k

@dma_k แน่นอนคำตอบนั้นดี แต่ไม่ได้แตะประเด็นสำคัญที่คุณกล่าวถึง
zinking

@Georgi มีการใช้งานที่มีอยู่แล้วหรือไม่ที่เราสามารถนำกลับมาใช้ใหม่ได้
เลื่อน

1
มันจะมีประโยชน์มากหากคุณสามารถระบุตัวอย่างโค้ดจาวาได้อย่างแม่นยำฉันกำลังมองหาวิธียกเลิกการโหลดคลาสโดยใช้ CustomClassLoader แต่ไม่มีโชค
Sriharsha grv

@ Sriharshag.rv คุณได้ลองและนำตัวอย่างมาใช้หรือไม่?
niaomingjian

17

ฉันเขียน classloader ที่กำหนดเองซึ่งเป็นไปได้ที่จะยกเลิกการโหลดแต่ละคลาสโดยไม่ต้อง GCing classloader Jar Class Loader


ได้ผลเหมือนจับใจ :) มีวิธีใดที่จะยกเลิกการโหลดคลาสไฟล์ทั้งหมดของไฟล์ jar หรือไม่?
Ercksen

น่าเสียดายที่ไม่ใช่ในขณะนี้ แต่จะมองเข้าไปที่มัน อาจจะอยู่ในรุ่นอนาคต
Kamran

โดยวิธีการที่ฉันพบวิธีแก้ปัญหาเล็กน้อย หากคุณมีหนึ่งJarClassLoaderไฟล์สำหรับทุกไฟล์ jar ที่คุณโหลดคุณสามารถเรียกgetLoadedClasses()ใช้จากนั้นวนซ้ำแต่ละไฟล์และยกเลิกการโหลด
Ercksen

12

Classloaders อาจเป็นปัญหาที่ยุ่งยาก คุณสามารถพบปัญหาโดยเฉพาะอย่างยิ่งถ้าคุณใช้ตัวโหลดคลาสหลายตัวและไม่มีการโต้ตอบที่ชัดเจนและเข้มงวด ฉันคิดว่าเพื่อที่จะสามารถยกเลิกการโหลดคลาสที่คุณจะต้องลบการอ้างอิงทั้งหมดไปยังคลาสใด ๆ (และอินสแตนซ์ของพวกเขา) ที่คุณกำลังพยายามที่จะยกเลิกการโหลด

คนส่วนใหญ่ต้องไปทำประเภทของการสิ้นสุดสิ่งนี้ขึ้นโดยใช้OSGi OSGi นั้นทรงพลังจริงๆและมีน้ำหนักเบาอย่างน่าประหลาดใจและใช้งานง่าย


7

คุณสามารถยกเลิกการโหลด ClassLoader แต่คุณไม่สามารถยกเลิกการโหลดคลาสที่เฉพาะเจาะจง โดยเฉพาะอย่างยิ่งคุณไม่สามารถยกเลิกการโหลดคลาสที่สร้างใน ClassLoader ที่ไม่ได้อยู่ในการควบคุมของคุณ

ถ้าเป็นไปได้ผมแนะนำให้ใช้ ClassLoader ของคุณเองเพื่อให้คุณสามารถถอดออกได้


4

คลาสมีการอ้างอิงที่ชัดเจนโดยนัยเกี่ยวกับอินสแตนซ์ ClassLoader ของพวกเขาและในทางกลับกัน พวกเขาจะถูกเก็บรวบรวมขยะเช่นเดียวกับวัตถุ Java คุณจะไม่สามารถลบแต่ละคลาสได้

คุณจะได้รับการรั่วไหลของหน่วยความจำเช่นเคย การอ้างอิงที่แข็งแกร่งใด ๆ ในคลาสหรือตัวโหลดคลาสของคุณจะทำให้ทุกอย่างรั่วไหล สิ่งนี้เกิดขึ้นกับการใช้ Sun ของ ThreadLocal, java.sql.DriverManager และ java.beans เป็นต้น


-1

หากคุณกำลังดูอยู่ว่าคลาสการยกเลิกการโหลดทำงานในJConsoleหรืออะไรให้ลองเพิ่ม java.lang.System.gc()ที่ส่วนท้ายของตรรกะการยกเลิกการโหลดคลาสของคุณ มันก่อให้เกิด Garbage Collector อย่างชัดเจน


3
ระวัง: System.gc () ไม่จำเป็นต้องเรียก GC มันขอให้ jvm เริ่มต้นเท่านั้น แต่ไม่ได้บังคับใช้ และ IME มักจะไม่เริ่ม GC: - \
Juh_
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.