如何深入理解Java虛擬機(jī)JVM類加載初始化

如何深入理解Java虛擬機(jī)JVM類加載初始化,很多新手對此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

成都創(chuàng)新互聯(lián)公司一直在為企業(yè)提供服務(wù),多年的磨煉,使我們在創(chuàng)意設(shè)計(jì),成都全網(wǎng)營銷推廣到技術(shù)研發(fā)擁有了開發(fā)經(jīng)驗(yàn)。我們擅長傾聽企業(yè)需求,挖掘用戶對產(chǎn)品需求服務(wù)價(jià)值,為企業(yè)制作有用的創(chuàng)意設(shè)計(jì)體驗(yàn)。核心團(tuán)隊(duì)擁有超過10年以上行業(yè)經(jīng)驗(yàn),涵蓋創(chuàng)意,策化,開發(fā)等專業(yè)領(lǐng)域,公司涉及領(lǐng)域有基礎(chǔ)互聯(lián)網(wǎng)服務(wù)綿陽電信機(jī)房機(jī)柜租用、成都App定制開發(fā)、手機(jī)移動建站、網(wǎng)頁設(shè)計(jì)、網(wǎng)絡(luò)整合營銷。

1. Classloader的作用,概括來說就是將編譯后的class裝載、加載到機(jī)器內(nèi)存中,為了以后的程序的執(zhí)行提供前提條件。

2. 一段程序引發(fā)的思考:

風(fēng)中葉老師在他的視頻中給了我們一段程序,號稱是世界上所有的Java程序員都會犯的錯(cuò)誤。

詭異代碼如下:

Java代碼

package test01;         class Singleton {             public static Singleton singleton = new Singleton();         public static int a;         public static int b = 0;             private Singleton() {             super();             a++;             b++;         }             public static Singleton GetInstence() {             return singleton;         }         }         public class MyTest {             /**         * @param args         */        public static void main(String[] args) {             Singleton mysingleton = Singleton.GetInstence();             System.out.println(mysingleton.a);             System.out.println(mysingleton.b);         }         }

一般不假思索的結(jié)論就是,a=1,b=1。給出的原因是:a、b都是靜態(tài)變量,在構(gòu)造函數(shù)調(diào)用的時(shí)候已經(jīng)對a和b都加1了。答案就都是1。但是運(yùn)行完后答案卻是a=1,b=0。

下面我們將代碼稍微變一下

Java代碼

public static Singleton singleton = new Singleton();     public static int a;     public static int b = 0;

的代碼部分替換成

Java代碼

public static int a;     public static int b = 0;     public static Singleton singleton = new Singleton();

效果就是剛才預(yù)期的a=1,b=1。

為什么呢,這3句無非就是靜態(tài)變量的聲明、初始化,值的變化和聲明的順序還有關(guān)系嗎?Java不是面向?qū)ο蟮膯?怎么和結(jié)構(gòu)化的語言似地,順序還有關(guān)系。這個(gè)就是和Java虛擬機(jī)JVM加載類的原理有著直接的關(guān)系。

1. 類在JVM中的工作原理

要想使用一個(gè)Java類為自己工作,必須經(jīng)過以下幾個(gè)過程

1):類加載load:從字節(jié)碼二進(jìn)制文件——.class文件將類加載到內(nèi)存,從而達(dá)到類的從硬盤上到內(nèi)存上的一個(gè)遷移,所有的程序必須加載到內(nèi)存才能工作。將內(nèi)存中的class放到運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi),之后在堆區(qū)建立一個(gè)java.lang.Class對象,用來封裝方法區(qū)的數(shù)據(jù)結(jié)構(gòu)。這個(gè)時(shí)候就體現(xiàn)出了萬事萬物皆對象了,干什么事情都得有個(gè)對象。就是到了***層究竟是雞生蛋,還是蛋生雞呢?類加載的最終產(chǎn)物就是堆中的一個(gè)java.lang.Class對象。

2):連接:連接又分為以下小步驟

驗(yàn)證:出于安全性的考慮,驗(yàn)證內(nèi)存中的字節(jié)碼是否符合JVM的規(guī)范,類的結(jié)構(gòu)規(guī)范、語義檢查、字節(jié)碼操作是否合法、這個(gè)是為了防止用戶自己建立一個(gè)非法的XX.class文件就進(jìn)行工作了,或者是JVM版本沖突的問題,比如在JDK6下面編譯通過的class(其中包含注解特性的類),是不能在JDK1.4的JVM下運(yùn)行的。

準(zhǔn)備:將類的靜態(tài)變量進(jìn)行分配內(nèi)存空間、初始化默認(rèn)值。(對象還沒生成呢,所以這個(gè)時(shí)候沒有實(shí)例變量什么事情)

解析:把類的符號引用轉(zhuǎn)為直接引用(保留)

3):類的初始化: 將類的靜態(tài)變量賦予正確的初始值,這個(gè)初始值是開發(fā)者自己定義時(shí)賦予的初始值,而不是默認(rèn)值。

2. 類的主動使用與被動使用

以下是視為主動使用一個(gè)類,其他情況均視為被動使用!

1):初學(xué)者最為常用的new一個(gè)類的實(shí)例對象(聲明不叫主動使用)

2):對類的靜態(tài)變量進(jìn)行讀取、賦值操作的。

3):直接調(diào)用類的靜態(tài)方法。

4):反射調(diào)用一個(gè)類的方法。

5):初始化一個(gè)類的子類的時(shí)候,父類也相當(dāng)于被程序主動調(diào)用了(如果調(diào)用子類的靜態(tài)變量是從父類繼承過來并沒有復(fù)寫的,那么也就相當(dāng)于只用到了父類的東東,和子類無關(guān),所以這個(gè)時(shí)候子類不需要進(jìn)行類初始化)。

6):直接運(yùn)行一個(gè)main函數(shù)入口的類。

所有的JVM實(shí)現(xiàn)(不同的廠商有不同的實(shí)現(xiàn),有人就說IBM的實(shí)現(xiàn)比Sun的要好……)在***主動調(diào)用類和接口的時(shí)候才會初始化他們。

如何深入理解Java虛擬機(jī)JVM類加載初始化
如何深入理解Java虛擬機(jī)JVM類加載初始化

1. 類的加載方式

1):本地編譯好的class中直接加載

2):網(wǎng)絡(luò)加載:java.net.URLClassLoader可以加載url指定的類

3):從jar、zip等等壓縮文件加載類,自動解析jar文件找到class文件去加載util類

4):從java源代碼文件動態(tài)編譯成為class文件

2. 類加載器

JVM自帶的默認(rèn)加載器

1):根類加載器:bootstrap,由C++編寫,所有Java程序無法獲得。

2):擴(kuò)展類加載器:由Java編寫。

3):系統(tǒng)類、應(yīng)用類加載器:由Java編寫。

用戶自定義的類加載器:java.lang.ClassLoader的子類,用戶可以定制類的加載方式。每一個(gè)類都包含了加載他的ClassLoader的一個(gè)引用——getClass().getClassLoader()。如果返回的是null,證明加載他的ClassLoader是根加載器bootstrap。

如下代碼

如何深入理解Java虛擬機(jī)JVM類加載初始化

這里面的指針就是C++的指針

1. 回顧那個(gè)詭異的代碼

從入口開始看

Singleton mysingleton = Singleton.GetInstence();

是根據(jù)內(nèi)部類的靜態(tài)方法要一個(gè)Singleton實(shí)例。

這個(gè)時(shí)候就屬于主動調(diào)用Singleton類了。

之后內(nèi)存開始加載Singleton類

1):對Singleton的所有的靜態(tài)變量分配空間,賦默認(rèn)的值,所以在這個(gè)時(shí)候,singleton=null、a=0、b=0。注意b的0是默認(rèn)值,并不是咱們手工為其賦予的的那個(gè)0值。

2):之后對靜態(tài)變量賦值,這個(gè)時(shí)候的賦值就是我們在程序里手工初始化的那個(gè)值了。此時(shí)singleton = new Singleton();調(diào)用了構(gòu)造方法。構(gòu)造方法里面a=1、b=1。之后接著順序往下執(zhí)行。

3):

public static int a;       public static int b = 0;

a沒有賦值,保持原狀a=1。b被賦值了,b原先的1值被覆蓋了,b=0。所以結(jié)果就是這么來的。類中的靜態(tài)塊static塊也是順序地從上到下執(zhí)行的。

2. 編譯時(shí)常量、非編譯時(shí)常量的靜態(tài)變量

如下代碼

Java代碼

package test01;         class FinalStatic {             public static final int A = 4 + 4;             static {             System.out.println("如果執(zhí)行了,證明類初始化了……");         }         }         public class MyTest03 {             /**         * @param args         */        public static void main(String[] args) {             System.out.println(FinalStatic.A);         }         }

結(jié)果是只打印出了8,證明類并沒有初始化。反編譯源碼發(fā)現(xiàn)class里面的內(nèi)容是

public static final int A = 8;

也就是說編譯器很智能的、在編譯的時(shí)候自己就能算出4+4是8,是一個(gè)固定的數(shù)字。沒有什么未知的因素在里面。

將代碼稍微改一下

public static final int A = 4 + new Random().nextInt(10);

這個(gè)時(shí)候靜態(tài)塊就執(zhí)行了,證明類初始化了。在靜態(tài)final變量在編譯時(shí)不定的情況下。如果客戶程序這個(gè)時(shí)候訪問了該類的靜態(tài)變量,那就會對類進(jìn)行初始化,所以盡量靜態(tài)final變量盡量沒什么可變因素在里面1,否則性能會有所下降。

1. ClassLoader的剖析

ClassLoader的loadClass方法加載一個(gè)類不屬于主動調(diào)用,不會導(dǎo)致類的初始化。如下代碼塊

Java代碼

ClassLoader classLoader = ClassLoader.getSystemClassLoader();     Class clazz = classLoader.loadClass("test01.ClassDemo");

并不會讓類加載器初始化test01.ClassDemo,因?yàn)檫@不屬于主動調(diào)用此類。

ClassLoader的關(guān)系:

根加載器——》擴(kuò)展類加載器——》應(yīng)用類加載器——》用戶自定義類加載器

加載類的過程是首先從根加載器開始加載、根加載器加載不了的,由擴(kuò)展類加載器加載,再加載不了的有應(yīng)用加載器加載,應(yīng)用加載器如果還加載不了就由自定義的加載器(一定繼承自java.lang. ClassLoader)加載、如果自定義的加載器還加載不了。而且下面已經(jīng)沒有再特殊的類加載器了,就會拋出ClassNotFoundException,表面上異常是類找不到,實(shí)際上是class加載失敗,更不能創(chuàng)建該類的Class對象。

若一個(gè)類能在某一層類加載器成功加載,那么這一層的加載器稱為定義類加載器。那么在這層類生成的Class引用返回下一層加載器叫做初始類加載器。因?yàn)榧虞d成功后返回一個(gè)Class引用給它的服務(wù)對象——也就是調(diào)用它的類加載器??紤]到安全,父委托加載機(jī)制。

如何深入理解Java虛擬機(jī)JVM類加載初始化

ClassLoader加載類的原代碼如下

Java代碼

protected synchronized Class loadClass(String name, boolean resolve)         throws ClassNotFoundException         {         // First, check if the class has already been loaded         Class c = findLoadedClass(name);         if (c == null) {             try {             if (parent != null) {                 c = parent.loadClass(name, false);             } else {                 c = findBootstrapClassOrNull(name);             }             } catch (ClassNotFoundException e) {                     // ClassNotFoundException thrown if class not found                     // from the non-null parent class loader                 }                 if (c == null) {                 // If still not found, then invoke findClass in order                 // to find the class.                 c = findClass(name);             }         }         if (resolve) {             resolveClass(c);         }         return c;         }

初始化系統(tǒng)ClassLoader代碼如下

Java代碼

private static synchronized void initSystemClassLoader() {         if (!sclSet) {             if (scl != null)             throw new IllegalStateException("recursive invocation");                 sun.misc.Launcher l = sun.misc.Launcher.getLauncher();             if (l != null) {             Throwable oops = null;             scl = l.getClassLoader();                 try {                 PrivilegedExceptionAction a;                 a = new SystemClassLoaderAction(scl);                         scl = (ClassLoader) AccessController.doPrivileged(a);                 } catch (PrivilegedActionException pae) {                 oops = pae.getCause();                     if (oops instanceof InvocationTargetException) {                     oops = oops.getCause();                 }                 }             if (oops != null) {                 if (oops instanceof Error) {                 throw (Error) oops;                 } else {                     // wrap the exception                     throw new Error(oops);                 }             }             }             sclSet = true;         }         }

它里面調(diào)用了很多native的方法,也就是通過JNI調(diào)用底層C++的代碼。

當(dāng)一個(gè)類被加載、連接、初始化后,它的生命周期就開始了,當(dāng)代表該類的Class對象不再被引用、即已經(jīng)不可觸及的時(shí)候,Class對象的生命周期結(jié)束。那么該類的方法區(qū)內(nèi)的數(shù)據(jù)也會被卸載,從而結(jié)束該類的生命周期。一個(gè)類的生命周期取決于它Class對象的生命周期。由Java虛擬機(jī)自帶的默認(rèn)加載器(根加載器、擴(kuò)展加載器、系統(tǒng)加載器)所加載的類在JVM生命周期中始終不被卸載。所以這些類的Class對象(我稱其為實(shí)例的模板對象)始終能被觸及!而由用戶自定義的類加載器所加載的類會被卸載掉!

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進(jìn)一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。

網(wǎng)站欄目:如何深入理解Java虛擬機(jī)JVM類加載初始化
本文地址:http://bm7419.com/article46/jdcpeg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序、全網(wǎng)營銷推廣網(wǎng)站排名、品牌網(wǎng)站制作、網(wǎng)站改版、電子商務(wù)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化