مقدمة
يجب أن يعلم الجميع أنه عند بدء تشغيل مشروع Java ، سيجد JVM الطريقة الرئيسية ، وتحميل ملف الفئة وملف الفئة في حزمة JAR المرجعية وفقًا للمكالمات بين الكائنات (تنقسم الخطوات إلى التحميل والتحقق والتحضير والتحليل والتهيئة والاستخدام والتفريغ). تفتح منطقة الطريقة الذاكرة لتخزين بنية بيانات وقت تشغيل الفئة (بما في ذلك المتغيرات الثابتة ، والطرق الثابتة ، والتجمعات الثابتة ، وهياكل الفئة ، وما إلى ذلك) ، وفي الوقت نفسه ، يتم إنشاء كائن الفئة المقابل في الكومة للإشارة إلى بنية بيانات وقت تشغيل الفئة المقابلة في منطقة الطريقة.
لتلخيص أبسط جملة ، فإن عملية تحميل الفئة هي أن JVM يقرأ ملف الفئة Bytecode من خلال تدفقات IO بناءً على مسار ملف الفئة المطلوب ، وحقنه في الذاكرة من خلال سلسلة من خطوات التحليل والتهيئة. تشمل اللواذين الفئة في Java: BootstrapClassloader (الطبقة العليا) ، و exclassloader ، و appclassloader ، و classloader المعرفة من قبل المستخدم (الطبقة السفلية). بالنسبة لأنواع مختلفة من حزم الجرة (أو ملفات الفئة) ، سيكون لدى JVM أنواع مختلفة من محمل الفصول الدراسية التي يجب تحميلها.
العلاقة المقابلة على النحو التالي:
يتم استخدام bootstrapclassloader لتحميل الفئات المطلوبة لتشغيل JVM:
java_home/jre/lib/sourses.jar java_home/jre/lib/jfr.jar: java_home/jre/lib/classes
يتم استخدام extclassloader لتحميل فئات التمديد:
../java/extensions: ../java_home/jre/lib/ext:
يتم استخدام AppClassLoader لتحميل الفئات التي تم إنشاؤها تحت ClassPath والفئات المشار إليها في حزمة JAR في مشروعنا.
يتم تحميل تحميل الفئة بأكملها من خلال آلية تسمى وفد الوالدين.
على سبيل المثال ، يتم تحميل فئة بواسطة أقل محمل المستوى (ClassLoader المعرفة من قبل المستخدم). سيقوم هذا المحمل أولاً بالاتصال بمستوى المحمل السابق (AppClassloader) للتحميل ، وسيستمر تسليم AppClassloader إلى المستوى العلوي (extclassloader) للتحميل حتى bootstrapclassloader. إذا لم يتمكن classpath بواسطة bootstrapclassloader من العثور على هذه الفئة ، فسيتم تسليمه إلى المحمل (extclassloader) للطبقة التالية للتحميل. إذا تعذر العثور على هذه الفئة ، فسيستمر تسليمها إلى الطبقة التالية (AppClassLoader) للتحميل. وما إلى ذلك ، إذا لم يتمكن جهاز تحميل Classloader المعرفة من قبل المستخدم من ذلك ، فسيقوم البرنامج بإلقاء classnotfounderror.
تظهر عملية التحميل بأكملها على النحو التالي:
(تم نقل الصورة من: https://www.cnblogs.com/xing901022/p/4574961.html)
تتبع رمز المصدر لمصدر تحميل الفئة كما يلي (تم تبسيط رمز المصدر بشكل مناسب هنا). يمكن للقراء النقر فوق الكود المصدر لعرضه:
حزمة java.lang.classloader ؛ استيراد ...... الفئة المحمية <؟> loadclass (اسم السلسلة ، حل منطقي) يلقي classnotfoundexception {synchronized (getClassloadinglock (name)) {// first ، ابحث في ذاكرة الجهاز الافتراضي ما إذا كانت هذه الفئة قد تم تحميلها ... المشكلة الرئيسية مع CASHE CACHES! ! ! الفئة <؟> c = findloadedClass (name) ؛ if (c == null) {long t0 = system.nanotime () ؛ جرب {if (parent! = null) {// اسمحوا تحميل الطبقة السابقة أولاً c = parent.loadClass (name ، false) ؛ } else {c = findBootStrapClassornull (name) ؛ }} catch (classnotfoundException e) {// classnotfoundException shrown إذا لم يتم العثور على فئة // من loader من فئة الوالدين غير null} if (c == null) {// calendar طريقة FindClass التي تم تنفيذها بواسطة هذا التحميل لتحميل c = findClass (الاسم) ؛ }} if (حل) {solveClass (c) ؛ } return c ؛ }}يمكنك أن تقدر تمامًا عملية آلية تفويض الوالدين في الكود المصدري ، وتم تمييز أهم ثلاث جمل من التعليمات البرمجية:
إذا كان المستخدم يحتاج إلى محمل مخصص وتحميل ملف الفئة للمسار المحدد ، فهو بحاجة إلى ورث أداة تحميل classloader وتنفيذ طريقة FindClass (اسم السلسلة). كمثال:
package com.linuxidc.Utils ؛ import java.io.bytearrayoutputstream ؛ import java.io.fileInputStream ؛ import java.io.ioException ؛ java.io.inputStream ؛ serviceClassloader public classloader {private string classpath ؛ serviceClassloader (String classpath) {this.classpath = classPath ؛ } /*** أعد كتابة طريقة FindClass لفئة الأصل. سوف يقوم LoadClass للفئة الأصل بتسمية هذه الطريقة */ Override الفئة المحمية <؟> FindClass (اسم السلسلة) يلقي ClassNotFoundException {class <؟> c = null ؛ byte [] classData = getClassData (name) ؛ if (classData! = null) {c = defereClass (name ، classData ، 0 ، classData.Length) ؛ } آخر {رمي classnotfoundException () ؛ } return c ؛ } // اقرأ ملف الفئة من خلال دفق IO وقم بتحويله إلى بايت بايت بايت خاص [] getClassData (اسم السلسلة) {String path = classpath + "/" + name.replace ('.' ، '/') + ".class" ؛ inputStream isTream = null ؛ bytearrayoutputstream bytearrayoutputStream = جديد bytearrayoutputstream () ؛ حاول {isTream = جديد fileInputStream (path) ؛ Byte [] Buffer = New Byte [1024] ؛ int temp = 0 ؛ بينما ((temp = isTream.Read (buffer))! =-1) {bytearrayoutputstream.write (buffer ، 0 ، temp) ؛ } if (bytearrayoutputStream! = null) {return bytearrayoutputstream.tobytearray () ؛ }} catch (استثناء e) {E.PrintStackTrace () ؛ } أخيرًا {try {if (isTream! = null) {isTream.close () ؛ }} catch (ioException e) {E.PrintStackTrace () ؛ } جرب {if (bytearrayoutputStream! = null) {bytearrayoutputstream.close () ؛ }} catch (ioException e) {E.PrintStackTrace () ؛ }} الإرجاع null ؛ }}الرمز لاستخدام تحميل الفئة كما يلي:
serviceClassloader serviceClassloader = جديد serviceClassloader ("C:/myClass") ؛ czlass <؟> c = serviceClassloader.loadClass ("com.linuxidc.service.myclass") ؛إذا كنت تستخدم نفس كائن serviceClassloader لتحميل نفس ملف الفئة عدة مرات ، فإن كائن الفئة بعد كل تحميل هو نفسه! ومع ذلك ، إذا قامت ClassLoader المخصصة الجديدة بتحميل نفس ملف الفئة ، فسيتم إرجاع كائن فئة مختلف في كل مرة.
ملاحظة: لا يمكن وضع ملف الفئة الذي تريد تحميله في دليل ClassPath وأي دليل فرعي ، وإلا فإنه سيتم تحميله أولاً بواسطة AppClassloader (وذلك لأن تحميل الفئة يعتمد آلية التفويض الأصل ، ويمكن لـ AppClassloader تحميل جميع ملفات الفئة تحت classpath). في كل مرة ، يتم تحميل نفس AppClassloader ، لذلك ستكون هناك مشاكل في التخزين المؤقت للصف.
هذا يحل مشكلة استخدام الانعكاس مباشرة عند تحميل فئة JVM.
لخص
ما سبق هو المحتوى الكامل لهذه المقالة. آمل أن يكون لمحتوى هذه المقالة قيمة مرجعية معينة لدراسة أو عمل الجميع. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل. شكرا لك على دعمك إلى wulin.com.