Java ClassLoader คืออะไร
เป็นที่ทราบกันอยู่แล้วว่า Java นั้นรันบน JVM (Java Virtual Machine) เมื่อเรา Compile Java class มันจะแปลงร่างเป็น bytecode แล้วเก็บไฟล์เป็น “.class” หลังจากนั้น เมื่อเราต้องการใช้ class ตัว Java class loader จะทำการโหลดคลาสนั้น ๆ ลงใน Memory…
บทความนี้จะอธิบายแบบผิว ๆ นะครับไม่เจาะลึกลงไปว่า
Load ลง Memory แล้วมันจะไปอยู่ไหน หรือ JVM Memory Model
ไม่ว่าจะเป็น Metaspace, Code Cache, Eden space, etc. ก็ไม่พูดถึงMahasak Pijittum ได้กล่าวไว้ว่า เด็กสมัยนี้เกิดมาในยุค RAM ถูก CPU ไม่แพง ไม่เคยได้ tuning JVM เอง เลยไม่รู้ว่าจริง ๆ แล้วเบื้องหลังมันทำงานอะไรยังไง
ก่อนจะเข้าเรื่อง “รู้หรือไม่ Kotlin, Scala, Groovy, Clojure ล้วนแล้วแต่รันบน JVM ฮ่า ๆ ขายของเก่ง”
ClassLoader Subsystem
ClassLoader subsystem ถือเป็น Core ของ JVM ที่ใช้ในการ loading/reading “.class” ไฟล์
ในส่วนของ ClassLoader Subsystem ได้ถูกแบ่งออกเป็น 3 ส่วน Loading, Linking และ Initialization
Loading
เราแบ่ง Loading build-in ClassLoader ใน Java ออกเป็น 3 ประเภท
- Bootstrap ClassLoader – โหลดคลาสจาก internal JDK โดยทั่วไปมันมักจะโหลดมาจากคลาสหลัก ๆ ทั่วไป เช่น “java.lang.*” หรือพวก “package class” $JAVA_HOME/lib/rt.jar
- Extensions ClassLoader – โหลดคลาสจาก extensions JDK directory โดยมากจะโหลดมาจาก $JAVA_HOME/lib/ext
- Application or System ClassLoader – โหลดคลาสจาก classpath ปัจจุบัน โดยที่สามารถ invoke classpath ได้ด้วย command -cp หรือ -classpath
ใครไม่รู้จัก classpath แนะนำให้ไปอ่านก่อน พักหลังผมมักเจอว่า เหล่านักพัฒนาซอฟแวร์เรา ไม่รู้จักกันหลายคน
Linking
ในส่วนนี้จะเป็นการทำ Linking class หรือพวก interface ที่เกี่ยวข้องเพื่อที่จะ allocation new data structure โดยแบ่งเป็น 3 หมวดย่อย Verification, Preparation, Resolution
Initialization
ส่วนนี้จะเป็นกระบวนการขั้นสุดท้ายของ Class loading ตัวแปรต่าง ๆ จะถูกกำหนดค่า และถูกเรียกจากคลาสแม่ ไปยังคลาสลูก
How ClassLoader Work
ClassLoader เป็นส่วนหนึ่งของ JRE (Java Runtime Environment) เมื่อตัว JVM ทำการ Request class ตัว Class Loader จะทำการวิ่งไปหา class แล้วนำตัว class เข้าไปสู่ runtime ถ้าหากไม่สามารถโหลดคลาสได้ มันจะทำการ request ไปยังคลาสแม่ไปเรื่อย ๆ ถ้าหากหายังไงก็หาไม่เจอ… เราก็จะได้เห็นภาพคุ้นตาครับ java.lang.NoClassDefFoundError ไม่ก็ java.lang.ClassNotFoundException นั่นเอง
java.lang.NoClassDefFoundError เกิดขึ้นเมื่อ Java runtime system พยายามจะ load definition ของ class แต่ไม่สามารถใช้งานได้
java.lang.ClassNotFoundException เกิดขึ้นเมื่อ application พยายามโหลด Class ที่ runtime Class.forName() หรือ loadClass() หรือ findSystemClass() ใน classpath ไม่เจอ
เอาไว้คราวหน้าจะเล่าแบบละเอียดแต่ถ้าอยากศึกษาก่อน แนะนำให้ไปอ่านได้ ที่นี่ ครับ
Delegation Model
เป็นการมอบหมายงาน เมื่อมีการ Request application class ไปสู่ JVM ตัว JVM จะทำการ เรียก System ClassLoader จากนั้นก็จะไปเรียก Extension ClassLoader แล้วก็ไปที่ Bootstrap ClassLoader
JVM -> System ClassLoader -> Extension ClassLoader -> Bootstrap ClassLoader
Uniqueness
ในส่วนนี้เป็นผลต่อเนื่องมาจาก Delegation model ตัว Class ที่ถูก load มาแล้วนั้น ไม่ควรถูกโหลดอีกครั้งโดย Child Class Loader
Visibility
ตัว Children ClassLoader จะมองเห็นได้แค่ ตัว Parent Class Loader เท่านั้น
ตัวอย่างเช่น ถ้า Class A ถูกโหลดมาจาก Application class loader และ Class B โหลดจาก Extention class loader ทั้ง Class A และ Class B ก็จะปรากฎให้เห็นเช่นกัน แต่ว่า Class B จะมองเห็นได้แค่เฉพาะ Extension class loader ที่เกี่ยวข้องกันเท่านั้น
เพื่อให้เข้าใจมากขึ้น เรามาดูรูปกันดีกว่าตัวอย่าง: JVM ทำการ Request Customer.class
- มันจะวิ่งไปที่ System ClassLoader ตัว System ClassLoader จะทำการมอบหมายไปให้กับ Extension ClassLoader จากนั้นก็ส่งไปยัง Bootstrap ClassLoader
- Bootstrap ClassLoader ทำการ Load Customer.class ถ้าเจอก็จะทำการ Load to memory
- ในกรณีที่ Bootstrap ClassLoader หา Customer.class ไม่เจอ ตัว Extension ClassLoader ก็จะทำการหาจาก $JAVA_HOME/jre/ext ถ้าเจอก็จะทำการ Load to memory
- ในกรณีที่ Extension Class Loader หา Customer.class ไม่เจอ ตัว System ClassLoader ก็จะทำการหาเองจาก classpath ถ้าเจอก็จะทำการ Load to memory
- ในกรณีที่ System ClassLoader หา Customer.class ไม่เจออีก เราก็จะได้เห็นภาพคุ้นตา “java.lang.ClassNotFoundException”
Conclusion
สรุปสั้น ๆ ได้ว่า ClassLoader Subsystem แบ่งออกได้ 3 ส่วน Loading, Linking และ Initialzation
ใน Loading ตัว Java ClassLoader แบ่งออกเป็น 3 ส่วน
- Bootstrap ClassLoader เอาของจาก $JAVA_HOME/lib/rt.jar
- Extension ClassLoader เอาของจาก $JAVA_HOME/lib/ext
- Application or System ClassLoader เอาของจาก classpath
ใน Linking แบ่งออกเป็น 3 ส่วน Verification, Preparation, Resolution
ผมได้รับแรงบันดารใจในการเขียนเรื่องนี้มาจาก น้องในทีมเดินมาบอกว่า พี่กิ๊กครับ ผมอยากอ่าน Java ClassLoader ถ้าพี่เขียนผมสัญญาว่าผมจะอ่านทุกบรรทัด (ในใจผมคิดว่าแล้วทำไมไม่หาอ่านเองงงงงง 5555) แต่ก็ถือเป็นเรื่องดีที่ได้ทบทวน แล้วก็ได้แบ่งปันเรื่องราวนี้สู่มวลมนุษยชาติ
อันที่จริงมันยังมี JIT Complier, Hotspot, Hot Reload อีกที่ไม่ได้กล่าวถึง… ที่เป็นเหตุผลว่า… ทำไม JAVA เร็วส์ ครั้งหน้าผมอาจจะมาต่อในส่วนของ Custom ClassLoader
หากอ่านถึงตรงนี้ยังคาใจ ไปอ่านต่อที่ References ด้านล่างต่อกันได้เลย