Java sınıfı yükleyici mekanizmasının derinlemesine anlaşılması

Önsöz

Java'daki sınıf yükleme mekanizmasının, Java sanal makinesinin temel bileşenlerinden biri olduğu söylenebilir.JVM sanal makinesinin mimarisine hakim olmak ve anlamak, Java dilini temel ilkeler açısından anlamamıza yardımcı olacaktır. Bu nedenle yeni bir şey öğreniyoruz. Prensibi anlamazsan ezberlemeye güveneceksin. Birkaç gün içinde unutacağına inanıyorum.

Java, platformlar arası bir dildir ve JVM sanal makinesi bunda çok önemli bir rol oynamaktadır.Yazdığımız .java dosyası için, derleme sırasında ikili sınıf dosyaya dönüştürülecektir.Ayrıca buna bayt kodu (word Bölüm kodu), ardından bu sınıf dosyaları JVM sanal makineye nasıl yüklenir ve nasıl çalıştırılır?

Bu, makalemizin bugün odaklanacağı bilgi olan Java sınıf yükleyiciyi tanıtır. Bundan önce, JVM'nin yürütme mimarisini gözden geçiririz. İnternetten bir resim ödünç almak sezgisel olarak anlamamıza yardımcı olabilir:

Java sanal makinesinin çekirdeği üç önemli bileşenden oluşur:

(1) Sınıf yükleme sistemi

(2) Çalışma zamanı veri alanı

(3) Yürütme motoru

Burada, sınıf yükleme mekanizmasını, çalışma zamanı veri alanını ve yürütme motorundaki GC toplayıcının algoritmasını ve ilkesini anlamaya ve bu konuda uzmanlaşmaya odaklanmamız gerekir.

Çalışma zamanı veri alanı bir önceki makalede tanıtılmıştı. Gc algoritması ve prensibi tek bir sohbet için bir makale bırakacak.Bu makalede, sınıf yükleyici mekanizmasına odaklanacağız.

Yazımızın başında yazdığımız java kaynak dosyalarının derleme sonrasında binary bytecode sınıf dosyalarına dönüştürüleceğinden bahsetmiştik.Kullanmak istiyorsak kullanılmadan önce sınıf yükleyici tarafından yüklenip işlenmeleri gerekiyor.

Neden bir sınıf yükleyiciye ihtiyacınız var?

Geniş bir konsepte göre, Java dilinde yalnızca iki sınıf yükleyici vardır:

(1) Bootstrap CLassloder (önyükleme sınıfı yükleyici)

(2) Kullanıcı Tanımlı Sınıf Yükleyici (kullanıcı tanımlı sınıf yükleyici)

Önyükleme sınıfı yükleyicinin kendisi JVM belirtiminin bir parçasıdır. İşletim sistemi platformuyla ilgilidir ve türü (Java API sınıfları ve arayüzleri dahil) yüklemek için işletim sisteminin uygulanmasına bağlıdır, bu nedenle Java'daki önyükleme sınıfı yükleyici yalnızca yerel olarak uygulanabilir Tüm sınıf yükleyicilerin üst yükleyicisi olmasına rağmen, Java'da uygulanmaz, bu nedenle Java'daki önyükleyici null değerini döndürür.

Java önyükleme yükleyicisi kesinlikle kapalıdır, çünkü rolü, yaygın olarak kullanılan java.lang.xxx ve diğer ilgili sınıfları içeren rt.jar vb. Gibi temel Java çekirdek kitaplıklarını ve önyükleme sınıfı yüklemesini garanti eden kitaplığı yüklemektir. Tür güvenliği için, bir Long sınıfı, Java temel kitaplığının Long sınıfının yerini alacak şekilde özelleştirmek istiyorsanız, bu neredeyse imkansızdır.

Özel sınıf yükleyici mekanizması, bazı özel işlevler elde etmek için yükleyiciyi özelleştirmemize izin veren çok esnek bir uzatma mekanizması sağlar.

Neden özel bir sınıf yükleyiciye ihtiyacınız var?

İşte birkaç senaryo:

(1) Şifreleme. Bayt kodu şifrelemesi için, Java sınıfı dosyalar kolaylıkla derlenebilmektedir.Güvenliği artırmak için, derleme sırasında şifreleme algoritmaları ekleyebilir, ikili dosyaların kodlamasını değiştirebilir ve ardından şifrelenmiş dosyaları yüklemek için özel bir yükleyici tanımlayabiliriz. Dosyayı açın, yüklemeden önce ikili bayt kodunu çözün ve güvenliği artırabilmek için yükleyin.

(2) Sınıf dosyasını standart olmayan bir şekilde yükleyin. Örneğin, sınıf dosyalarımız bir veritabanında, FTP'de saklanır veya bir web sitesinden indirilir.

(3) Çalışma zamanında sistemin dışında bir sınıfı dinamik olarak yükleyin ve çalıştırın.

(4) Aynı uygulamada, ortamın veya kaynakların yalıtımı, sınıf yükleyici aracılığıyla gerçekleştirilir.

(5) Sınıf yükleyici aracılığıyla esnek bir tak-çıkar mekanizma gerçekleştirilir.

Java Sınıf Yükleyicisinin Ebeveyn Yetkilendirme Mekanizması

Yukarıdan, özel sınıf yükleyicinin gücünü görebiliriz.Özel sınıf yükleyiciyi uygulamadan önce, Java'daki sınıf yükleyicinin sınıfları nasıl yüklediğini anlamamız gerekir.

Java'daki ClassLoader sınıfı, özel bir sınıf yükleyiciyi uygulamanın anahtarıdır.SınıfLoader sınıfı, özel bir sınıf yükleyicinin genel bir tanımını sağlayan soyut bir sınıftır. Ana alt sınıfları aşağıdaki gibidir:

ClassLoader SecureClassLoader URLClassLoader ExtClassLoader AppClassLoader

Java platformunun özel uygulamasına göre, gerçek sınıf yükleyici sırası aşağıdaki gibidir:

Burada herkesin, sınıf yükleyicileri sırasının sözde miras ilişkisi olmadığı, aslında mantıksal bir kombinasyon ilişkisi olduğu gerçeğine dikkat etmesi gerekir.

Daha önce belirtildiği gibi, önyükleme sınıfı yükleyici tüm yükleyicilerin ön koşuludur, ancak Java dilinde belirli bir sınıf yoktur, çünkü işletim sistemiyle ilgilidir, bu nedenle yerel bir yöntem uygulamasıdır. Ancak, Java'daki tüm sınıf yükleyicilerin gerçek üst yükleyicisidir ve yüklenen kaynak yolu şöyledir:

% JAVA_HOME% / jre / lib

Ardından ExtClassLoader yükleyiciye bakarız, yükleme yolu

% JAVA_HOME% / jre / lib / ext veya java.ext.dirs özelliğinde yapılandırılan yol

Son olarak, AppClassLoader yükleyici, yüklenen kaynak yolu:

Geçerli sınıf yolunun yolu

Yukarıdaki analiz sayesinde, sınıf yükleyicinin özünün aslında yüklenen yolun altındaki kaynak dosyası olduğunu görebiliriz.Yukarıdaki birkaç sınıf yükleme yolu için cevabı Java sanal makine başlangıç sınıfı Başlatıcı kaynak kodunda bulabiliriz:

Önyükleme sınıfı yükleyicinin yolu:

System.getProperty ("sun.boot.class.path");

ExtClassLoader sınıf yükleyicisinin yolu şudur:

System.getProperty ("java.ext.dirs")

Son olarak, AppClassLoader sınıf yükleyicisinin yolu şudur:

System.getProperty ("java.class.path")

Aşağıdaki test yöntemi ile doğrulanabilir:

genel statik void showClassLoaderForeachPath () { System.out.println (); // BoostrapClassLoader String split = System.getProperty ("sun.boot.class.path"). Split (":"); for (Dize verileri: bölme) { System.out.println (veri); } System.out.println ("===================="); // ExeClassLoader String split1 = System.getProperty ("java.ext.dirs"). Split (":"); for (Dize verileri: split1) { System.out.println (veri); } System.out.println ("===================="); // AppClassLoader String split2 = System.getProperty ("java.class.path"). Split (":"); for (Dize verileri: split2) { System.out.println (veri); } System.out.println ("================"); }

Sonra gelişigüzel bir test sınıfı tanımlar ve bu sınıfın yükleyicisine bakarız:

genel statik void showClassLoaderPath () { System.out.println (ClassLoaderTest.class.getClassLoader ()); System.out.println (ClassLoaderTest.class.getClassLoader (). GetParent ()); System.out.println (ClassLoaderTest.class.getClassLoader (). GetParent (). GetParent ()); System.out.println ("------------------------------------"); System.out.println (int.class.getClassLoader ()); System.out.println (Long.class.getClassLoader ()); }

Çıktı sonucu:

sun.misc.Launcher$AppClassLoader@511d50c0 sun.misc.Launcher$ExtClassLoader@5e481248 boş ------------------------------------ boş boş

Özel sınıflarımızın hepsinin AppClassLoader tarafından yüklendiğini, ancak AppClassLoader tarafından kimlerin yüklendiğini görebilirsiniz?

İkinci kod satırında, ExtClassLoader tarafından yüklendiğini görebilirsiniz .. Sınıf yükleyici hiyerarşisinin kalıtım dışı ilişkisinin burada tekrar vurgulandığına dikkat edin.

Sonra ExtClassLoader sınıf yükleyicisinin üst sınıfına baktık ve çıktının boş olduğunu gördük. Daha önce de belirtildiği gibi, önyükleyici yerel olarak uygulandı, bu nedenle Java'da erişilebilir değil, bu yüzden boş.

Bu noktaya kadar sorumuz, sınıf yükleyicinin neden miras alınmadığına odaklanıyor, çünkü yukarıdaki sınıf diyagramında, AppClassLoader ve ExtClassLoader eşit kardeşlerdir, öyleyse AppClassLoader neden üst sınıf yükleyici olarak ExtClassLoader'ı kullanıyor?

Cevap kaynak kodda yatıyor.Öncelikle ClassLoader soyut sınıfının yapıcısına bakın:

// 1 korumalı ClassLoader () { this (checkCreateClassLoader (), getSystemClassLoader ()); } // 2 korumalı ClassLoader (ClassLoader parent) { this (checkCreateClassLoader (), parent); } // 3 özel ClassLoader (Kullanılmayan Void, ClassLoader ebeveyni) { this.parent = ebeveyn; eğer (ParallelLoaders.isRegistered (this.getClass ())) { parallelLockMap = yeni ConcurrentHashMap < > (); package2certs = new ConcurrentHashMap < > (); etki alanları = Collections.synchronizedSet (yeni HashSet < Koruma Etki Alanı > ()); assertionLock = new Object (); } Başka { // daha ince bir kilit yok; sınıf yükleyici örneğini kilitleyin parallelLockMap = null; package2certs = new Hashtable < > (); etki alanları = yeni HashSet < > (); assertionLock = this; } }

Hem parametresiz hem de tek parametreli oluşturucuların iki parametreli oluşturucuyu çağırdığını gördük. İki parametreli yapıcının ikinci parametresi tam olarak belirtilen üst sınıf yükleyicidir. Parametresiz yapıcı kullanılırsa, varsayılan çağrı şudur:

genel statik ClassLoader getSystemClassLoader () { initSystemClassLoader (); eğer (scl == null) { boş döndür; } SecurityManager sm = System.getSecurityManager (); eğer (sm! = null) { checkClassLoaderPermission (scl, Reflection.getCallerClass ()); } dönüş scl; }

Sonra initSystemClassLoader yöntemine bakın. Bu yöntemdeki kilit nokta, sun.misc.Launcher çağrıldıktan sonra ClassLoader örneğinin bu sınıftan elde edilmesidir:

sun.misc.Launcher l = sun.misc.Launcher.getLauncher (); eğer (l! = null) { Fırlatılabilir oops = boş; scl = l.getClassLoader (); }

Ardından Launcher sınıfının yapıcısının nasıl tanımlandığına bakarız:

genel Başlatıcı () { Launcher.ExtClassLoader var1; Deneyin { // 1 var1 = Launcher.ExtClassLoader.getExtClassLoader (); } catch (IOException var10) { yeni InternalError ("Uzantı sınıfı yükleyici yaratılamadı", var10); } Deneyin { // 2 this.loader = Launcher.AppClassLoader.getAppClassLoader (var1); } catch (IOException var9) { yeni InternalError ("Uygulama sınıfı yükleyici yaratılamadı", var9); } // ........ }

İkinci sıraya odaklanın. AppClassLoader'ı ayarlayan üst yükleyici ExtClassLoader'dır ve ExtClassLoader ayarlanmamıştır.Sistemin başlangıç değeri boştur.Ayrıca, atama tamamlandıktan sonra, AppClassLoader örneği diğer tüm varsayılan özel olanlara atanır. Sınıf yükleyicinin üst yükleyicisi, dolayısıyla bir sınıf yükleyiciyi özelleştirirsek, belirtilmemişse üst yükleyicisi AppClassLoader olur.

ClassLoader sınıfının aşağıdaki gibi birkaç önemli yöntemi vardır:

loadClass (), sınıfları yüklemek için üst temsilci yöntemini kullanır. defineClass (), bir bayt akışını Class sınıfının bir örneğine dönüştürür. findClass (), yükleyici yolundan işlenecek sınıfı arar. findLoadedClass (), bir sınıfın yüklenip yüklenmediğini sorgular. getResourceAsStream () InputStream'e bir kaynak dosyası okuyun

Sınıf yükleyicinin hiyerarşisini ve ana yöntemlerini bilerek, bir sınıfı yüklediğimizde sınıf yükleyici nasıl çalışır?

Odak, ClassLoader sınıfının loadClass yöntemidir. Şimdi kaynak koduna bakalım:

Kamusal sınıf < ? > loadClass (Dize adı) ClassNotFoundException { return loadClass (ad, yanlış); }

Aşırı yüklenmiş yöntem şöyle adlandırılır:

korumalı Sınıf < ? > loadClass (Dize adı, mantıksal çözümleme) ClassNotFoundException atar { Eşzamanlılığı önlemek için kilitleme gerçekleştirin. senkronize (getClassLoadingLock (name)) { Önce sınıfın yüklenip yüklenmediğine karar verin ve yüklendiyse doğrudan geri dönün. Sınıf < ? > c = findLoadedClass (ad); eğer (c == null) { long t0 = System.nanoTime (); Deneyin { Yüklenmemişse ve bir üst yükleyici varsa. eğer (ebeveyn! = null) { Özyinelemeli yükleme, üst sınıf yükleyiciye devredilir. c = parent.loadClass (ad, yanlış); } Başka { Üst yükleyici yoksa, sorgulama için önyükleme sınıfı yükleyiciyi emanet edin. c = findBootstrapClassOrNull (ad); } } catch (ClassNotFoundException e) { // Sınıf bulunmazsa ClassNotFoundException atılır // boş olmayan üst sınıf yükleyiciden } Sonunda, hala sorgu yok ve kendi yükleme yolundan yükleniyor. eğer (c == null) { // Hala bulunamazsa, sırasıyla findClass'ı çağırın // sınıfı bulmak için. long t1 = System.nanoTime (); c = findClass (ad); // bu tanımlayıcı sınıf yükleyicidir; istatistikleri kaydedin sun.misc.PerfCounter.getParentDelegationTime (). addTime (t1-t0); sun.misc.PerfCounter.getFindClassTime (). addElapsedTimeFrom (t1); sun.misc.PerfCounter.getFindClasses (). increment (); } } Sınıfın ayrıştırılması gerekip gerekmediğini belirleyin. if (çözümle) { resolClass (c); } dönüş c; } }

sonuç olarak:

(1) Özel sınıf yükleyici veya geçerli sınıf yükleyici ilk önce sınıfın yüklenip yüklenmediğini belirler ve eğer yüklendiyse doğrudan geri döner, aksi takdirde üst yükleyiciye onu yüklemesi için emanet eder.

(2) Ana yükleyici (1) adımını tekrarlar, önce yüklenip yüklenmediğine karar verir, eğer yüklenmişse, doğrudan geri dönün, aksi takdirde, (1) adımını tekrarlayarak tekrar etmeye devam edin

(3) Üst yükleyici boşsa, önyükleme sınıfı yükleyiciye, yüklendiyse sorgulama görevi verilir, ardından doğrudan geri dönülür, aksi takdirde mevcut sınıf yükleyicinin yolu altında sorgulanır, eğer hala bulunamazsa, bir öncekine geri döner. Seviye, üst seviye aynı adımları gerçekleştirecektir.

(4) Sonda bulunmazsa, özel sınıf yükleyici yolu altında arama yapar ve bulursa geri döner, aksi takdirde ilgili sınıf bulunmayan istisna atar.

Sorgu sürecinin tamamı aşağıdaki gibidir, bir netizenden bir resim ödünç almak çok açıktır:

Yukarıdan da görebileceğiniz gibi, temsilci eylemi aşağıdan yukarıya doğru ve sorgu eylemi yukarıdan aşağıya doğru.Elbette, bir optimizasyon katmanı var, yani aşağıdan yukarıya, önce sınıfın yüklü olup olmadığını belirleyecektir. Doğrudan geri dönün, yukarı doğru yetkilendirmeye devam etmeye gerek yoktur, bu klasik ebeveyn yetkilendirme modelidir.

Ebeveynlerin emanet ettiği modelin anlamı ve yıkımı

Öncelikle, ebeveyn yetkilendirme modeli zorunlu bir kısıtlama değil, Java tasarımcıları tarafından geliştiricilere önerilen bir sınıf yükleyici uygulama yöntemidir.Java dünyasındaki çoğu sınıf yükleyici bu modeli izler. Java programlarının güvenli ve istikrarlı çalışmasını sağlamak için ebeveyn yetkilendirme modeli çok önemlidir.En büyük önemi Java platformunun güvenliğini ve istikrarını artırmaktır.Neden böyle söylüyorsunuz?

Üst temsilci modeli, Java sınıfının sınıf yükleyicisi ile birlikte öncelikli bir hiyerarşik ilişkiye sahip olmasını sağladığından, bir sınıf yüklenirken, model sıkı bir şekilde korunuyorsa, önce modelin üstündeki lider sınıf başlatılmalıdır. Yükleyici, yüklemeyi sorgular, böylece Object gibi bazı temel sınıflar için aynı sınıfın farklı sınıf yükleyici ortamlarında kullanılmasını sağlayabilir, ancak bu model mevcut değilse, örneğin bir bilgisayar korsanı bir Object sınıfı tanımlar veya siz Birden fazla Object sınıfı tanımlanmışsa, kullanım sırasında birden fazla kopya yüklenecektir.Sistemde birden fazla farklı Object sınıfı olacaktır.Java türü sistemdeki en temel davranış garanti edilemez ve uygulama tek parça haline gelecektir. Kafa karıştırıcı ve çok güvensiz.

Ana yetki modeli nasıl kırılır?

Spesifik uygulamada, yarı-shou üst modelini seçebilir veya seçemezsiniz.Eğer yarı-shou'yu seçerseniz, loadClass yönteminin mantığını değiştirmemeye çalışın ve yalnızca findClass yöntemini yeniden yazmanız gerekir, ancak ClassLoader sınıfını miras alırsanız, buna dikkat edin. LoadClass'ın delegasyon mantığı yazılır ve artık yukarıdaki delegasyon sorgusu gibi değildir, ancak başka herhangi bir sorgu yükleme moduna değiştirilir, bu durumda bu davranış üst delegasyon modelini yok edebilir.

Bir java.lang.String sınıfını özelleştirmek istediğinizi söylemiştiniz? Yapılabilir mi? Cevap evet, ancak bu tavsiye edilmiyor. Bir sınıf yükleyiciyi özelleştirerek, ardından üst yetki modelini yok ederek ve son olarak defineClass yöntemini geçersiz kılarak (bu yerel yöntemde kontrol kısıtlamaları da vardır), Java dilinin çeşitli kısıtlamalarını atlayarak , Hedefe ulaşabilir, ancak aslında büyük bir güvenlik riski vardır.Java ile başlayan paketteki temel veri türlerini yok etmek için bir neden yoktur.Bu davranış, ebeveyn delegasyon modelini yok eden en önemli davranıştır. Paket adları ve sınıf adları aynı olsa da, özel sınıf yükleyicinin String sınıfı ve JVM yerleşik String sınıfı, farklı sınıf yükleyiciler tarafından yüklendikleri için hala eşit değildir.

Yukarıdakiler elbette üst düzey imha vakalarıdır, bazıları ebeveyn yetkilendirme modelindeki eksikliklerden kaynaklanmaktadır.

Ebeveyn yetkisi modunda:

ClassLoader A- > Uygulama sınıfı yükleyici > Uzatma sınıfı yükleyici > Bootstrap sınıfı yükleyici

En soldaki sınıf en alttaki sınıftır.En üst sınıf tarafından yüklenen sınıflara erişebilirsiniz, ancak bunun tersi mümkün değildir.Ancak, gerçek geliştirme durumlarında, üst yükleyicinin alt yükleyicinin yüklemesini geri çağırması gerektiği ile karşılaşabilirsiniz. Uygulama sınıfı. Bu sorunun üstesinden gelmek için, ThreadContextClassLoader, üst temsilci modelinde tanıtılmıştır.Alttaki yükleyici setContextClassLoader ve getContextClassLoader of Thread aracılığıyla elde edilebilir, böylece bu problemi temeldeki yükleyici aracılığıyla yükleyerek önleyebilirsiniz.

Yaygın bir örnek: Java'daki SPI mekanizması veya java.sql'nin sürücü somutlaştırması örneği, bunların temel arabirimlerinin tümü Java önyükleme sınıfı yükleyici tarafından yüklenir, ancak bunların uygulanması çeşitli satıcılar tarafından sağlanır veya Konvansiyona göre, bu durumda, önyükleme sınıfı yükleyici, alt yükleyicinin sınıflarını (sınıf yolu) göremez, bu nedenle yalnızca alt yükleyicinin kendisi tarafından yüklenebilir, bu sefer üst yükleyicinin alt yüklemeyi kullanması gereken zamana eşdeğerdir. Sunucu tarafından yüklenen sınıf, arka kapıya eşdeğer olan üst yetki verme modelini dolaylı olarak yok eder.

Üst delegasyon modelini yok etmenin bir başka örneği, sıcak yükleme modudur. Kesintisiz servis veya kesintisiz servis güncelleme uygulamaları sorununu çözmek için, tipik uygulama senaryosu OSGI'dadır. Varsayılan olarak, yüklenen üst delegasyon modeli yenilenmeyecektir. Yüklendi, ancak bu, güncellemenin zamanında algılanmayacağı anlamına gelir.Dinamik güncelleme yapmanız gerekiyorsa, yüklenen sınıfın da yeniden yüklenmesi ve eski örneğin ve yeni örneğin durum verilerini kopyalama sorunu ele alınmalıdır. Bu model aynı zamanda ebeveyn yetkilendirme mekanizmasını da yok eder.

Son olarak, başka bir soruyu düşünelim, java sınıfı yükleme sistemi neden varsayılan olarak üç seviyeye ayrılıyor?

Bu üç sınıf yükleyici daha önce görülmüştür, yani önyükleme sınıfı, uzantı sınıfı ve uygulama sınıfı yükleyici Basitçe ifade etmek gerekirse, bu tasarımın amacı güvenlik içindir.Bu üç seviye farklı güven düzeylerini temsil eder. Örneğin, Java'nın çekirdek paketi en yüksek güvenlik düzeyine sahiptir, bunu ext uzantısı paketi ve son olarak uygulama düzeyi sınıfları izler. Daha önce de belirtildiği gibi, uygulama düzeyinde java ile başlayan bir paket türü tanımlarsanız, bu genellikle etkili olmayacaktır.Derlenebilse ve geçebilse bile, Java üst yetki modeli, onu değiştirmek için çeşitli girişimleri önlemek için çeşitli kontroller yapacaktır. Temel veri türü eylemleri.

Sınıf yükleme sisteminin aşama süreci ve yaşam döngüsü

Daha önce çok yazmıştım. Aslında bunların çoğu, ebeveyn delegasyon modelinin kendisinin artıları, eksileri veya anlamları hakkındadır. Bir sınıfın tüm yaşam döngüsüne göre, kabaca aşağıdaki aşamalara ayrılmıştır:

(1) Başlatma aşaması

Yaşam döngüsünün başlangıcında üç işlem vardır: yükleme, bağlama ve başlatma

(2) Orta Vadeli

Yaşam döngüsünün ara aşamaları arasında nesne somutlaştırma, çöp toplama ve sonlandırma yer alır.

(3) İmha aşaması

Yaşam döngüsünün sonunda, sanal makine çıktığında, sınıfın kaldırılması gerekir.

Yukarıdaki aşamalara göre, başlangıç aşamasının yükleme sürecini tanıttık.

Yükleme işlemi, sınıfın tam adı belirtilerek basitçe kabul edilebilir, diskteki ikili sınıf dosyası veya herhangi bir konum, java'nın dahili veri yapısına ayrıştırılır ve yığın belleğinin yöntem alanında depolanır ve ardından Sınıf oluşturulur. Sınıfın bir örneği, türü temsil eder.

Daha sonra bağlantı adımı vardır.Bu süreç üç adıma bölünür.İlk olarak, sınıf dosyasının formatının JVM uygulama belirtimine uygun olup olmadığını kontrol edin.Sonra hazırlama adımında sınıfın statik alanlarına varsayılan değerler atanır ve bellek alanı ayrılır. Adım herhangi bir Java kodunu yürütmez, yalnızca statik alanlara varsayılan değerleri atar. Çözümleme adımı isteğe bağlıdır Bu adım, sınıftaki sembolik referansları gerçek referanslarla değiştirecektir. Elbette, bu adım bir gecikmeyle de tetiklenebilir ve aynı zamanda, somutlaştırmadan sonra programa gerçekten başvurulduğunda da yürütmek mümkündür. Örneğin:

sınıf X { statik {System.out.println ("init sınıf X ...");} int foo () {dönüş 1;} Y çubuğu () {yeni Y () döndür;} }

X Sınıfı Y'ye karşılık gelirse, Sınıf X'te görünen tüm referanslar yüklenecektir.Reove = false ise, Sınıf Y şu anda yüklenmeyecek, ancak gerçekten kullanılana kadar bekleyecektir. Tembel bir yükleme stratejisi olan yüklenecek.

Son olarak, başlatma aşamasında, temeldeki jvm çağrısının karşılık gelen klinik komutunu başlatın.Bu sırada, statik blok çalıştırılacak ve statik alana belirttiğimiz varsayılan değer atanacaktır.

Belirli bir sınıf ilk kez kullanıldığında, belirli bir sınıfın başlatma davranışı tetiklenir. Altı durum vardır:

(1) Yeni operatör göründüğünde veya yansıtma, klonlama, seriyi kaldırma vb. Dahil olmak üzere örtük koşullar

(2) Sınıfın statik metodu denir

(3) Belirli bir sınıfın statik olarak değiştirilmiş bir sınıfı, alanı veya arayüzü kullanılır (final hariç, çünkü son ifade bir derleme zamanı sabitidir ve başlatılması derleme zamanında belirlenir)

(4) Yansıma yoluyla sınıftaki ilgili yöntemleri çağırın

(5) Alt sınıfın başlatılması, ana sınıfın başlatılmasını tetikleyecektir.

(6) Sınıfın ana yöntemi yürütülür

Yükleme, bağlantı ve başlatma sırası sıralı olmalıdır, yani bir sınıf başlatılacaksa, o zaman bağlanmalıdır.Eğer bir sınıf bağlanmak istiyorsa, önce yüklenmelidir.

Sınıf yükleyici, yüklenen sınıfları önbelleğe alır.Yükleme sırasında bir istisna veya sorun oluşursa, aktif olarak atılmayacaktır. Yalnızca sınıf ilk kez kullanıldığında atılmalıdır. Sınıf hiç olmamışsa Kullanın, o zaman bu istisna asla olmayacak.

Önceki adımların analizi sadece bu sınıfın kullanım şartlarına sahip olduğu ve ilk aşamanın hazırlandığı anlamına gelir.Aşağıdaki kullanım aşamasıdır:

Bu aşama esas olarak sınıfın somutlaştırılması ve örneğin ilklendirilmesidir.Bir sınıfı somutlaştırmak için genellikle aşağıdaki yöntemler vardır, yeni oluşturma, yansıma newInstance oluşturma, koni oluşturma ve getObject'in serisini kaldırma oluşturma. Örneklemenin çağrılmasından sonra, alt katman Aslında, ilk olarak üye değişkenlerini atayan, yapı taşını çalıştıran ve son olarak kurucuyu çalıştıran init talimatı çağrılır.

Uygulamamızda, nesnelere bellek ayırabiliriz, ancak belleği açıkça geri kazanamayız.Bu iş genellikle JVM çöp toplayıcı tarafından geri dönüştürülür. Belleği geri kazanırken, nesnenin finalize yöntemi ile yapabiliriz. Bazı çalışmalar sonrasında, geri kazanılan nesneler yine de kullanılabilir, bu dikkat gerektirir.

Son olarak, sınıfların boşaltma aşamasını da kısaca anlamamız gerekir çünkü yüklenen sınıflar ve arayüzler bellek kaynaklarını kullanmalıdırlar.Kısıtlama olmadan saklanırlarsa, programın hafızası kaçınılmaz olarak tükenecektir.Bu nedenle kullanılmayan nesneler üzerinde işlem yapmak gerekir. Geri dönüşüm, normal koşullar altında, önyükleme sınıfı yükleyici tarafından yüklenen nesneler ve sınıflar geri dönüştürülmez, çünkü bunlar Java programlarının çalıştırılması için temel oluşturur.Genellikle, AppClassLoader veya özel sınıf yükleyicimiz tarafından yüklenen sınıfların ve sınıfların geri dönüştürülmesi gerekir. Nesne, sonra nasıl geri dönüştürülür? Arkasında GC çöp toplayıcısı vardır. Basitçe ifade etmek gerekirse, bu sınıf için açık bir uygulama olmadığında veya erişilebilir bir yol olmadığında bu kaynak geçersiz kabul edilir. Şu anda, kaynakları geri almak için bu sınıf kaldırılabilir.

Yukarıdaki bilgileri öğrendikten sonra başka bir soruya bakalım Aşağıdaki iki yazma yöntemi arasındaki fark nedir?

Class.forName ("BazıSınıf"); ClassLoader.getSystemClassLoader (). LoadClass ("BazıSınıf");

Birincisi bir sınıfı yansıtmak, ikincisi ise bir sınıfı yüklemek için bir sınıf yükleyici kullanmaktır.

Yansımanın altında yatan yürütme yöntemi şudur:

Class.forName (className, true, currentLoader)

İkinci yöntem, sınıfın statik değişkenlerinin başlatılıp başlatılmayacağını ve statik blokların çalıştırılıp çalıştırılmayacağını temsil eder.

Sınıf yükleyicinin loadClass yönteminin temel uygulaması şudur:

loadClass (ad, yanlış)

İkinci parametrenin başlatma ile hiçbir ilgisi yoktur, yalnızca yükleme sırasında sembol referanslarını doğrudan referanslar olarak çözmek için yeniden yüklemenin yürütülmesi gerekip gerekmediğiyle ilgilidir.

Dolayısıyla, yansıma kullanıyorsanız, varsayılan, sınıf yükleyici başlatma aşamasının yükleme, bağlantı ve başlatma dahil olmak üzere üç adımını gerçekleştirmektir.

ClassLoader.loadClass () yönteminin doğrudan kullanımı, yük ve bağlantı dahil olmak üzere yalnızca sınıf yükleyicinin başlatma aşamasının iki adımını gerçekleştirmek içindir, ancak başlatma adımını gerçekleştirmez. Bu, dikkat gerektirir.

Tipik bir örnek, JDBC sürücüsünü kullanırken, onu yansıtma yoluyla başlatıyoruz. Sınıf, sınıf yükleyici aracılığıyla yüklenirse, başlatılmaz.

Sınıfın başlatma sırası hakkında

Sınıf yükleme mekanizmasına hakim olduktan sonra, Java sınıflarının başlatma sırasını anlamak bizim için çok kolaydır.Bir alt sınıf, ana sınıfı miras alırsa, alt sınıfın somutlaştırılması sırasında tüm sıra aşağıdaki gibidir:

Ana sınıf statik bloğu. Alt sınıf statik bloğu. Üst sınıf üye değişkenlerinin başlatılması. Ana yapı taşı. Ana sınıf yapıcısı. Alt sınıf üye değişkeni başlatma. Alt sınıf yapı taşı. Alt sınıf yapıcısı.

İlgilenen arkadaşlar kendileri doğrulayabilir, kodu buraya yapıştırmayacağım, gerekirse github'umdan indirebilirsiniz.

sonuç olarak

Bu makale temel olarak Java sınıf yükleyicinin ilgili bilgilerini tanıtır ve ebeveyn yetkilendirme mekanizmasının özellikleri ve öneminin yanı sıra eksikliklerini ve ebeveyn yetkilendirme modelinin nasıl yok edileceğini derinlemesine analiz eder.Ayrıca, sınıfın tüm yaşam döngüsünde yaşanan adımları da ayrıntılı olarak analiz eder. Son olarak, yansıma yüklemeyi kullanma ile sınıf yükleyici yüklemesi arasındaki fark açıklanır.Sınıfların yükleme mekanizmasına hakim olmak, Java dilini anlamak için çok yardımcıdır ve her Java geliştiricisinin ilerlemesinin tek yoludur.

Referans malzemeleri:

https://blog.csdn.net/briblue/article/details/54973413

https://www.artima.com/insidejvm/ed2/lifetype6.html

https://javatutorial.net/jvm-explained

https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.1

Orijinal metin WeChat kamu hesabında yayınlandı - Ben bir kuşatma bölümüyüm (woshigcs)

Hannan'ın bu fermente soya peyniri "somut olmayan kültürel miras" olarak ilan edildi, 4 kuşaktır aktardı ve yılda 13.000 şişe sattı
önceki
Ni Ni harika, beyaz gömlek ve pantolon o kadar iyi giyebiliyor ki, "kaliteli bir yüz" olmayı hak ediyor.
Sonraki
Kuru ürünler | Kullanıcı davranışına göre video kümeleme şeması
Oyunda, Di Lieba ona takıntılı, altın renkli pantolon ve ceket giyiyor, en iyi "patlayıcı saç modeli" çok yakışıklı.
Meitu Dağıtılmış Bitmap Uygulaması
14 yaşında, mavi ve beyaz dikişli etek giyen, duygu dolu büyük dalgalı kıvırcık saçlı bir kızla model olarak ilk kez sahneye çıktı.
GIF: Tereyağı Eli! Han Jiaqi, Xie Pengfei'ye ayrılması için bir hediye verdi, Suning 5-1 R&F
Popüler bilim | Bayes olasılık modellerinin listesi
Bao Wenjing "rüzgara karşı döndü", gül kırmızısı ceket ve kot pantolon, 32 yaşında peri kadar güzel
Meitu-On en uygun çözümü bulma fikri
Oyunda, binlerce yıldır Zheng Shuang'a aşık olmuş, bugün çizgili bir gömlek ve kot pantolon giyiyor ve gökyüzünü göstermek için güneş gözlüğü takıyor.
Pekin Film Festivali kapatıldı "Lucky Boy Bill", "Tiantan Ödülü" nün en iyi filmi oldu
Di Ali Reba düştükten sonra etkinliğe katıldı, siyah deri eteği kalçasından yeni geçti ama güzel bacakları ayakkabılarla mahvoldu.
RDD ilkeleri ve temel işlemler | Girişten ustaya Spark
To Top