JavaRush /Blog Java /Random-MS /Cara kelas dimuatkan dalam JVM
Aleksandr Zimin
Tahap
Санкт-Петербург

Cara kelas dimuatkan dalam JVM

Diterbitkan dalam kumpulan
Selepas bahagian paling sukar dalam kerja pengaturcara telah selesai dan aplikasi "Hello World 2.0" telah ditulis, yang tinggal hanyalah memasang kit pengedaran dan memindahkannya kepada pelanggan, atau sekurang-kurangnya kepada perkhidmatan ujian. Dalam pengedaran, semuanya adalah seperti yang sepatutnya, dan apabila kami melancarkan program kami, Mesin Maya Java muncul ke tempat kejadian. Bukan rahsia lagi bahawa mesin maya membaca arahan yang dibentangkan dalam fail kelas dalam bentuk bytecode dan menterjemahkannya sebagai arahan kepada pemproses. Saya bercadang untuk memahami sedikit tentang skema bytecode masuk ke dalam mesin maya.

Pemuat kelas

Ia digunakan untuk membekalkan kod bait yang disusun kepada JVM, yang biasanya disimpan dalam fail dengan sambungan .class, tetapi juga boleh diperoleh daripada sumber lain, contohnya, dimuat turun melalui rangkaian atau dijana oleh aplikasi itu sendiri. Cara kelas dimuatkan dalam JVM - 1Menurut spesifikasi Java SE, untuk menjalankan kod dalam JVM, anda perlu melengkapkan tiga langkah:
  • memuatkan bytecode daripada sumber dan mencipta contoh kelasClass

    ini termasuk mencari kelas yang diminta antara yang dimuatkan lebih awal, mendapatkan bytecode untuk memuatkan dan menyemak ketepatannya, mencipta contoh kelas Class(untuk bekerja dengannya semasa runtime), dan memuatkan kelas induk. Jika kelas induk dan antara muka belum dimuatkan, maka kelas berkenaan dianggap tidak dimuatkan.

  • mengikat (atau menghubungkan)

    Mengikut spesifikasi, peringkat ini dibahagikan kepada tiga peringkat lagi:

    • Pengesahan , ketepatan kod bait yang diterima disemak.
    • Penyediaan , memperuntukkan RAM untuk medan statik dan memulakannya dengan nilai lalai (dalam kes ini, permulaan eksplisit, jika ada, sudah berlaku pada peringkat permulaan).
    • Resolusi , resolusi pautan simbolik jenis, medan dan kaedah.
  • memulakan objek yang diterima

    di sini, tidak seperti perenggan sebelumnya, semuanya kelihatan jelas apa yang harus berlaku. Sudah tentu, ia akan menjadi menarik untuk memahami dengan tepat bagaimana ini berlaku.

Semua langkah ini dilakukan secara berurutan dengan keperluan berikut:
  • Kelas mesti dimuatkan sepenuhnya sebelum dipautkan.
  • Kelas mesti diuji dan disediakan sepenuhnya sebelum ia dimulakan.
  • Ralat resolusi pautan berlaku semasa pelaksanaan program, walaupun ia dikesan pada peringkat pemautan.
Seperti yang anda ketahui, Java melaksanakan pemuatan kelas yang malas (atau malas). Ini bermakna pemuatan kelas medan rujukan kelas yang dimuatkan tidak akan dilakukan sehingga aplikasi menemui rujukan eksplisit kepada mereka. Dalam erti kata lain, menyelesaikan pautan simbolik adalah pilihan dan tidak berlaku secara lalai. Walau bagaimanapun, pelaksanaan JVM juga boleh menggunakan pemuatan kelas bertenaga, i.e. semua pautan simbolik mesti diambil kira dengan segera. Ia adalah untuk titik ini keperluan terakhir digunakan. Perlu diingat juga bahawa resolusi pautan simbolik tidak terikat dengan mana-mana peringkat pemuatan kelas. Secara umum, setiap peringkat ini menghasilkan kajian yang baik; mari kita cuba memikirkan yang pertama, iaitu memuatkan kod bait.

Jenis-jenis Pemuat Java

Terdapat tiga pemuat standard di Java, setiap satunya memuatkan kelas dari lokasi tertentu:
  1. Bootstrap ialah pemuat asas, juga dipanggil Primordial ClassLoader.

    memuatkan kelas JDK standard daripada arkib rt.jar

  2. Extension ClassLoader – pemuat sambungan.

    memuatkan kelas sambungan, yang terletak dalam direktori jre/lib/ext secara lalai, tetapi boleh ditetapkan oleh sifat sistem java.ext.dirs

  3. System ClassLoader – pemuat sistem.

    memuatkan kelas aplikasi yang ditakrifkan dalam pembolehubah persekitaran CLASSPATH

Java menggunakan hierarki pemuat kelas, di mana akarnya, sudah tentu, asas. Seterusnya datang pemuat sambungan, dan kemudian pemuat sistem. Sememangnya, setiap pemuat menyimpan penunjuk kepada induk supaya dapat mewakilkan pemuatan kepadanya sekiranya ia sendiri tidak dapat melakukan ini.

ClassLoader kelas abstrak

Setiap pemuat, kecuali yang asas, adalah keturunan kelas abstrak java.lang.ClassLoader. Sebagai contoh, pelaksanaan pemuat sambungan ialah kelas sun.misc.Launcher$ExtClassLoader, dan pemuat sistem ialah sun.misc.Launcher$AppClassLoader. Pemuat asas adalah asli dan pelaksanaannya disertakan dalam JVM. Mana-mana kelas yang dilanjutkan java.lang.ClassLoaderboleh menyediakan caranya sendiri untuk memuatkan kelas dengan blackjack dan kelas yang sama ini. Untuk melakukan ini, adalah perlu untuk mentakrifkan semula kaedah yang sepadan, yang pada masa ini saya hanya boleh mempertimbangkan secara dangkal, kerana Saya tidak memahami isu ini secara terperinci. Di sini mereka:
package java.lang;
public abstract class ClassLoader {
    public Class<?> loadClass(String name);
    protected Class<?> loadClass(String name, boolean resolve);
    protected final Class<?> findLoadedClass(String name);
    public final ClassLoader getParent();
    protected Class<?> findClass(String name);
    protected final void resolveClass(Class<?> c);
}
loadClass(String name)salah satu daripada beberapa kaedah awam, yang merupakan titik masuk untuk memuatkan kelas. Pelaksanaannya bermuara kepada memanggil kaedah lain yang dilindungi loadClass(String name, boolean resolve), yang perlu dibatalkan. Jika anda melihat Javadoc kaedah yang dilindungi ini, anda boleh memahami sesuatu seperti berikut: dua parameter dibekalkan sebagai input. Satu ialah nama binari kelas (atau nama kelas yang layak sepenuhnya) yang perlu dimuatkan. Nama kelas ditentukan dengan senarai semua pakej. Parameter kedua ialah bendera yang menentukan sama ada resolusi pautan simbolik diperlukan. Secara lalai ia adalah false , yang bermaksud pemuatan kelas malas digunakan. Selanjutnya, menurut dokumentasi, dalam pelaksanaan lalai kaedah, panggilan dibuat findLoadedClass(String name), yang menyemak sama ada kelas telah dimuatkan sebelum ini dan, jika ya, mengembalikan rujukan kepada kelas ini. Jika tidak, kaedah pemuatan kelas pemuat induk akan dipanggil. Jika tiada pemuat dapat mencari kelas yang dimuatkan, setiap daripada mereka, mengikut urutan terbalik, akan cuba mencari dan memuatkan kelas itu, mengatasi findClass(String name). Ini akan dibincangkan dengan lebih terperinci dalam bab "Skim Pemuatan Kelas". Dan akhirnya, akhir sekali, selepas kelas telah dimuatkan, bergantung pada bendera resolusi , ia akan diputuskan sama ada untuk memuatkan kelas melalui pautan simbolik. Contoh yang jelas ialah peringkat Resolusi boleh dipanggil semasa peringkat pemuatan kelas. Sehubungan itu, dengan memanjangkan kelas ClassLoaderdan mengatasi kaedahnya, pemuat tersuai boleh melaksanakan logiknya sendiri untuk menghantar bytecode ke mesin maya. Java juga menyokong konsep pemuat kelas "semasa". Pemuat semasa ialah pemuat yang memuatkan kelas yang sedang dilaksanakan. Setiap kelas mengetahui pemuat mana yang dimuatkan dan anda boleh mendapatkan maklumat ini dengan memanggilnya String.class.getClassLoader(). Untuk semua kelas aplikasi, pemuat "semasa" biasanya adalah sistem.

Tiga Prinsip Pemuatan Kelas

  • Perwakilan

    Permintaan untuk memuatkan kelas dihantar kepada pemuat induk dan percubaan untuk memuatkan kelas itu sendiri dibuat hanya jika pemuat induk tidak dapat mencari dan memuatkan kelas. Pendekatan ini membolehkan anda memuatkan kelas dengan pemuat yang sedekat mungkin dengan yang asas. Ini mencapai keterlihatan kelas maksimum. Setiap pemuat menyimpan rekod kelas yang dimuatkan olehnya, meletakkannya dalam cachenya. Set kelas ini dipanggil skop.

  • Keterlihatan

    Pemuat hanya melihat kelas "nya" dan kelas "ibu bapa" dan tidak tahu tentang kelas yang dimuatkan oleh "anak"nya.

  • Keunikan

    Kelas hanya boleh dimuatkan sekali. Mekanisme delegasi memastikan bahawa pemuat yang memulakan pemuatan kelas tidak membebankan kelas yang sebelum ini dimuatkan ke dalam JVM.

Oleh itu, apabila menulis pemuat butnya, pembangun harus berpandukan tiga prinsip ini.

Skim pemuatan kelas

Apabila panggilan untuk memuatkan kelas berlaku, kelas ini dicari dalam cache kelas yang telah dimuatkan bagi pemuat semasa. Jika kelas yang dikehendaki belum dimuatkan sebelum ini, prinsip delegasi memindahkan kawalan kepada pemuat induk, yang terletak satu tahap lebih tinggi dalam hierarki. Pemuat induk juga cuba mencari kelas yang dikehendaki dalam cachenya. Jika kelas telah dimuatkan dan pemuat mengetahui lokasinya, maka objek Classkelas itu akan dikembalikan. Jika tidak, carian akan diteruskan sehingga ia mencapai pemuat but asas. Jika pemuat asas tidak mempunyai maklumat tentang kelas yang diperlukan (iaitu, ia masih belum dimuatkan), kod bait kelas ini akan dicari di lokasi kelas yang diketahui oleh pemuat yang diberikan, dan jika kelas itu tidak dapat dimuatkan, kawalan akan kembali kepada pemuat anak, yang akan cuba memuatkan daripada sumber yang diketahuinya. Seperti yang dinyatakan di atas, lokasi kelas untuk pemuat asas ialah perpustakaan rt.jar, untuk pemuat sambungan - direktori dengan sambungan jre/lib/ext, untuk sistem satu - CLASSPATH, untuk pengguna ia boleh menjadi sesuatu yang berbeza . Oleh itu, kemajuan pemuatan kelas pergi ke arah yang bertentangan - dari pemuat akar ke pemuat semasa. Apabila kod bait kelas ditemui, kelas tersebut dimuatkan ke dalam JVM dan contoh jenis diperoleh Class. Seperti yang anda boleh lihat dengan mudah, skema pemuatan yang diterangkan adalah serupa dengan pelaksanaan kaedah di atas loadClass(String name). Di bawah anda boleh melihat rajah ini dalam rajah.
Cara kelas dimuatkan dalam JVM - 2

Sebagai kesimpulan

Dalam langkah pertama mempelajari bahasa, tidak ada keperluan khusus untuk memahami cara kelas dimuatkan dalam Java, tetapi mengetahui prinsip asas ini akan membantu anda mengelakkan keputusasaan apabila menghadapi ralat seperti ClassNotFoundExceptionatau NoClassDefFoundError. Nah, atau sekurang-kurangnya memahami secara kasar apa punca masalah itu. Oleh itu, pengecualian ClassNotFoundExceptionberlaku apabila kelas dimuatkan secara dinamik semasa pelaksanaan program, apabila pemuat tidak dapat mencari kelas yang diperlukan sama ada dalam cache atau sepanjang laluan kelas. Tetapi ralat NoClassDefFoundErroradalah lebih kritikal dan berlaku apabila kelas yang diperlukan tersedia semasa penyusunan, tetapi tidak kelihatan semasa pelaksanaan program. Ini boleh berlaku jika program terlupa memasukkan perpustakaan yang digunakannya. Nah, hakikat memahami prinsip struktur alat yang anda gunakan dalam kerja anda (tidak semestinya rendaman yang jelas dan terperinci dalam kedalamannya) menambah sedikit kejelasan kepada pemahaman proses yang berlaku di dalam mekanisme ini, yang, dalam giliran, membawa kepada penggunaan alat ini dengan yakin.

Sumber

Bagaimana ClassLoader Berfungsi dalam Java Secara keseluruhannya adalah sumber yang sangat berguna dengan pembentangan maklumat yang boleh diakses. Memuatkan kelas, ClassLoader artikel yang agak panjang, tetapi dengan penekanan pada cara membuat pelaksanaan pemuat anda sendiri dengan yang sama ini. ClassLoader: pemuatan dinamik kelas Malangnya, sumber ini tidak tersedia sekarang, tetapi di sana saya menemui gambar rajah yang paling mudah difahami dengan skema pemuatan kelas, jadi saya tidak boleh tidak menambahnya. Spesifikasi Java SE: Bab 5. Memuatkan, Memaut dan Memulakan
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION