คำนำ
ทุกคนควรรู้ว่าเมื่อโครงการ Java เริ่มต้นขึ้น JVM จะค้นหาวิธีการหลักและโหลดไฟล์คลาสและไฟล์คลาสในแพ็คเกจ JAR ที่อ้างอิงตามการโทรระหว่างวัตถุ (ขั้นตอนจะถูกแบ่งออกเป็นโหลดการตรวจสอบการเตรียมการแยกวิเคราะห์การเริ่มต้นใช้งานและการขนถ่าย) พื้นที่วิธีการเปิดหน่วยความจำเพื่อจัดเก็บโครงสร้างข้อมูลรันไทม์ของคลาส (รวมถึงตัวแปรคงที่วิธีการคงที่พูลคงที่โครงสร้างคลาส ฯลฯ ) และในเวลาเดียวกันวัตถุคลาสที่สอดคล้องกันจะถูกสร้างขึ้นในฮีปเพื่อชี้ไปที่โครงสร้างข้อมูลรันไทม์คลาสที่สอดคล้องกันในพื้นที่เมธอด
เพื่อสรุปในประโยคที่ง่ายที่สุดกระบวนการโหลดคลาสคือ JVM อ่านไฟล์ bytecode คลาสผ่านสตรีม IO ตามเส้นทางของไฟล์คลาสที่ต้องการและฉีดเข้าไปในหน่วยความจำผ่านชุดของขั้นตอนการแยกวิเคราะห์และการเริ่มต้น คลาสโหลดเดอร์ใน Java รวมถึง: BootstrapClassLoader (ชั้นบนสุด), extclassloader, AppClassLoader และ classloader ที่กำหนดโดยผู้ใช้ (เลเยอร์ด้านล่าง) สำหรับแพ็คเกจ JAR ประเภทต่าง ๆ (หรือไฟล์คลาส) JVM จะมีตัวโหลดคลาสที่แตกต่างกันในการโหลด
ความสัมพันธ์ที่สอดคล้องกันมีดังนี้:
Bootstrapclassloader ใช้สำหรับโหลดคลาสที่จำเป็นสำหรับการรัน JVM:
java_home/jre/lib/resources.jar: java_home/jre/lib/rt.jar: java_home/jre/lib/sunrasign.jar: java_home/jre/lib/jsse.jar: java_home/lib/lib/lib/lib/lib/lib/lib/lib java_home/jre/lib/jfr.jar: java_home/jre/lib/คลาส
ExtClassLoader ใช้ในการโหลดคลาสส่วนขยาย:
../java/extensions: ../java_home/jre/lib/ext: ../library/java/extensions:/network/library/java/extensions: ../system/library/java/extensions
AppClassLoader ใช้เพื่อโหลดคลาสที่สร้างขึ้นภายใต้คลาส Path และคลาสที่อ้างอิงในแพ็คเกจ JAR ในโครงการของเรา
การโหลดคลาสทั้งหมดถูกโหลดผ่านกลไกที่เรียกว่าการมอบหมายหลัก
ตัวอย่างเช่นคลาสจะถูกโหลดโดยตัวโหลดระดับต่ำสุด (คลาสที่กำหนดโดยผู้ใช้) ตัวโหลดนี้จะเรียกตัวโหลดระดับก่อนหน้า (AppClassLoader) ก่อนสำหรับการโหลดและ AppClassLoader จะยังคงส่งมอบไปยังระดับบน (extclassloader) สำหรับการโหลดจนกว่า bootstrapclassloader หาก classpath ที่โหลดโดย bootstrapclassloader ไม่สามารถหาคลาสนี้ได้มันจะถูกส่งไปยังตัวโหลด (extclassloader) ของเลเยอร์ถัดไปสำหรับการโหลด หากไม่พบคลาสนี้มันจะยังคงส่งมอบให้กับเลเยอร์ถัดไป (AppClassLoader) สำหรับการโหลด และอื่น ๆ หากคลาสที่ผู้ใช้กำหนดไม่สามารถหาคลาสนี้ได้โปรแกรมจะโยน classnotFoundError
กระบวนการโหลดทั้งหมดจะแสดงดังนี้:
(ภาพอ้างอิงจาก: https://www.cnblogs.com/xing901022/p/4574961.html)
การติดตามซอร์สโค้ดของแหล่งโหลดคลาสมีดังนี้ (ซอร์สโค้ดได้ง่ายขึ้นอย่างเหมาะสมที่นี่) ผู้อ่านสามารถคลิกที่ซอร์สโค้ดเพื่อดู:
แพ็คเกจ java.lang.classloader; นำเข้า ...... คลาสที่ได้รับการป้องกัน <?> loadclass (ชื่อสตริง, การแก้ไขบูลีน) พ่น classnotfoundexception {ซิงโครไนซ์ (getClassLoadingLock (ชื่อ)) {// ก่อนค้นหาในหน่วยความจำเครื่องเสมือน - - คลาส <?> c = findloadedClass (ชื่อ); if (c == null) {long t0 = system.nanotime (); ลอง {ถ้า (parent! = null) {// ให้โหลดเลเยอร์โหลดก่อนหน้า c = parent.loadclass (ชื่อ, เท็จ); } else {c = findbootstrapclassornull (ชื่อ); }} catch (classnotFoundException e) {// classnotFoundException โยนถ้าคลาสไม่พบ // จากตัวโหลดคลาสแม่ที่ไม่ใช่ NULL} ถ้า (c == null) {// ปฏิทินวิธี findClass ที่นำมาใช้โดยโหลดเดอร์นี้เพื่อโหลด c = findClass (ชื่อ); }} ถ้า (แก้ไข) {ResolveClass (c); } return c; -คุณสามารถชื่นชมกระบวนการของกลไกการมอบหมายผู้ปกครองในซอร์สโค้ดได้อย่างเต็มที่และมีการทำเครื่องหมายประโยคที่สำคัญที่สุดสามประโยค:
หากผู้ใช้ต้องการตัวโหลดที่กำหนดเองและโหลดไฟล์คลาสของพา ธ ที่ระบุเขาจำเป็นต้องสืบทอดคลาสโหลดและใช้เมธอด findClass (ชื่อสตริง) เป็นตัวอย่าง:
แพ็คเกจ com.linuxidc.utils; นำเข้า java.io.ByTeArrayOutputStream; นำเข้า java.io.fileinputstream; นำเข้า java.io.ioException; นำเข้า Java.io.InputStream; ServiceClassloader สาธารณะ Public ServiceClassLoader (String classPath) {this.classPath = classPath; } /*** เขียนวิธี findclass ของคลาสแม่ loadclass ของคลาสพาเรนต์จะเรียกวิธีนี้ */ @Override คลาสที่ได้รับการป้องกัน <?> findClass (ชื่อสตริง) พ่น classnotFoundException {class <?> c = null; ไบต์ [] classData = getClassData (ชื่อ); if (classdata! = null) {c = defeleclass (ชื่อ, classdata, 0, classdata.length); } else {โยน classnotFoundException ใหม่ (); } return c; } // อ่านไฟล์คลาสผ่านสตรีม IO และแปลงเป็นไบต์ไบต์ไบต์ส่วนตัว [] getClassData (ชื่อสตริง) {สตริงพา ธ = classPath + "/" + name.replace ('.', '/') + ".class"; INPUTSTREAM ISTREAM = NULL; ByTeArrayOutputStream ByTeArrayOutputStream = ใหม่ byteArrayOutputStream (); ลอง {istream = new FileInputStream (พา ธ ); ไบต์ [] บัฟเฟอร์ = ไบต์ใหม่ [1024]; int temp = 0; ในขณะที่ ((temp = istream.read (บัฟเฟอร์))! =-1) {byteArrayOutputStream.write (บัฟเฟอร์, 0, temp); } if (byTeArrayOutputStream! = null) {return byteArrayUtputStream.tobyteArray (); }} catch (exception e) {e.printstacktrace (); } ในที่สุด {ลอง {ถ้า (istream! = null) {istream.close (); }} catch (ioexception e) {e.printstacktrace (); } ลอง {if (byteArrayOutputStream! = null) {byteArrayOutputStream.close (); }} catch (ioexception e) {e.printstacktrace (); }} return null; -รหัสสำหรับการใช้คลาสโหลดเดอร์มีดังนี้:
ServiceClassloader ServiceClassloader = ใหม่ serviceClassloader ("C:/myclass"); czlass <?> c = serviceClassloader.loadclass ("com.linuxidc.service.myclass");หากคุณใช้วัตถุ ServiceClassloader เดียวกันเพื่อโหลดไฟล์คลาสเดียวกันหลายครั้งวัตถุคลาสหลังจากแต่ละโหลดจะเหมือนกัน! อย่างไรก็ตามหากคลาสที่กำหนดเองที่แตกต่างกันใหม่โหลดไฟล์คลาสเดียวกันวัตถุคลาสอื่นจะถูกส่งคืนในแต่ละครั้ง
หมายเหตุ: ไฟล์คลาสที่คุณต้องการโหลดไม่สามารถวางไว้ในไดเรกทอรี classpath และไดเรกทอรีย่อยใด ๆ มิฉะนั้นจะถูกโหลดก่อนโดย AppClassLoader (นี่เป็นเพราะการโหลดคลาสใช้กลไกการมอบหมายหลักและ AppClassloader สามารถโหลดไฟล์คลาสทั้งหมดภายใต้ ClassPath) ในแต่ละครั้งจะมีการโหลด appclassloader เดียวกันดังนั้นจะมีปัญหาการแคชในชั้นเรียน
สิ่งนี้จะแก้ปัญหาของการใช้การสะท้อนโดยตรงเมื่อคลาส JVM โหลด
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com