自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

深入Java虛擬機(jī)JVM類加載初始化學(xué)習(xí)筆記

開(kāi)發(fā) 后端
本篇文章主要介紹Java虛擬機(jī)JVM類加載初始化,希望對(duì)大家有所幫助。

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

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

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

詭異代碼如下:

Java代碼

 

  1. package test01;     
  2.     
  3. class Singleton {     
  4.     
  5.     public static Singleton singleton = new Singleton();     
  6.     public static int a;     
  7.     public static int b = 0;     
  8.     
  9.     private Singleton() {     
  10.         super();     
  11.         a++;     
  12.         b++;     
  13.     }     
  14.     
  15.     public static Singleton GetInstence() {     
  16.         return singleton;     
  17.     }     
  18.     
  19. }     
  20.     
  21. public class MyTest {     
  22.     
  23.     /**    
  24.      * @param args    
  25.      */    
  26.     public static void main(String[] args) {     
  27.         Singleton mysingleton = Singleton.GetInstence();     
  28.         System.out.println(mysingleton.a);     
  29.         System.out.println(mysingleton.b);     
  30.     }     
  31.     
  32. }    

 

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

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

Java代碼

 

  1. public static Singleton singleton = new Singleton();     
  2. public static int a;     
  3. public static int b = 0;    

 

的代碼部分替換成

Java代碼

 

  1. public static int a;     
  2. public static int b = 0;     
  3. public static Singleton singleton = new Singleton();    

 

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

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

1. 類在JVM中的工作原理

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

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

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

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

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

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

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

2. 類的主動(dòng)使用與被動(dòng)使用

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

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

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

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

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

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

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

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

 

 

 

 

1. 類的加載方式

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

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

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

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

2. 類加載器

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

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

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

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

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

如下代碼

 

 

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

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

從入口開(kāi)始看

Singleton mysingleton = Singleton.GetInstence();

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

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

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

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

2):之后對(duì)靜態(tài)變量賦值,這個(gè)時(shí)候的賦值就是我們?cè)诔绦蚶锸止こ跏蓟哪莻€(gè)值了。此時(shí)singleton = new Singleton();調(diào)用了構(gòu)造方法。構(gòu)造方法里面a=1、b=1。之后接著順序往下執(zhí)行。

3):

 

  1. public static int a;  
  2.  
  3.     public static int b = 0;  

 

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

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

如下代碼

Java代碼

  1. package test01;     
  2.     
  3. class FinalStatic {     
  4.     
  5.     public static final int A = 4 + 4;     
  6.     
  7.     static {     
  8.         System.out.println("如果執(zhí)行了,證明類初始化了……");     
  9.     }     
  10.     
  11. }     
  12.     
  13. public class MyTest03 {     
  14.     
  15.     /**    
  16.      * @param args    
  17.      */    
  18.     public static void main(String[] args) {     
  19.         System.out.println(FinalStatic.A);     
  20.     }     
  21.     
  22. }    

 

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

public static final int A = 8;

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

將代碼稍微改一下

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

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

1. ClassLoader的剖析

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

Java代碼

 

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

 

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

ClassLoader的關(guān)系:

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

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

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

 

 

ClassLoader加載類的原代碼如下

Java代碼

  1. protected synchronized Class loadClass(String name, boolean resolve)     
  2.     throws ClassNotFoundException     
  3.     {     
  4.     // First, check if the class has already been loaded     
  5.     Class c = findLoadedClass(name);     
  6.     if (c == null) {     
  7.         try {     
  8.         if (parent != null) {     
  9.             c = parent.loadClass(name, false);     
  10.         } else {     
  11.             c = findBootstrapClassOrNull(name);     
  12.         }     
  13.         } catch (ClassNotFoundException e) {     
  14.                 // ClassNotFoundException thrown if class not found     
  15.                 // from the non-null parent class loader     
  16.             }     
  17.             if (c == null) {     
  18.             // If still not found, then invoke findClass in order     
  19.             // to find the class.     
  20.             c = findClass(name);     
  21.         }     
  22.     }     
  23.     if (resolve) {     
  24.         resolveClass(c);     
  25.     }     
  26.     return c;     
  27.     }    

 

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

Java代碼

 

  1. private static synchronized void initSystemClassLoader() {     
  2.     if (!sclSet) {     
  3.         if (scl != null)     
  4.         throw new IllegalStateException("recursive invocation");     
  5.             sun.misc.Launcher l = sun.misc.Launcher.getLauncher();     
  6.         if (l != null) {     
  7.         Throwable oops = null;     
  8.         scl = l.getClassLoader();     
  9.             try {     
  10.             PrivilegedExceptionAction a;     
  11.             a = new SystemClassLoaderAction(scl);     
  12.                     scl = (ClassLoader) AccessController.doPrivileged(a);     
  13.             } catch (PrivilegedActionException pae) {     
  14.             oops = pae.getCause();     
  15.                 if (oops instanceof InvocationTargetException) {     
  16.                 oops = oops.getCause();     
  17.             }     
  18.             }     
  19.         if (oops != null) {     
  20.             if (oops instanceof Error) {     
  21.             throw (Error) oops;     
  22.             } else {     
  23.                 // wrap the exception     
  24.                 throw new Error(oops);     
  25.             }     
  26.         }     
  27.         }     
  28.         sclSet = true;     
  29.     }     
  30.     }   

 

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

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

【編輯推薦】

  1. 詳解JVM的內(nèi)存管理機(jī)制
  2. 深入Java核心:JVM中的棧和局部變量
  3. 專家答疑 Tomcat的JVM內(nèi)存大小如何設(shè)置?
  4. 你不知道的5個(gè)JVM命令行標(biāo)志
責(zé)任編輯:金賀 來(lái)源: JavaEye博客
相關(guān)推薦

2012-01-18 11:24:18

Java

2024-03-08 08:26:25

類的加載Class文件Java

2012-11-14 09:57:46

JavaJava虛擬機(jī)JVM

2019-03-05 14:59:42

Java虛擬機(jī)加載類

2010-03-15 14:24:59

StackHeapJVM

2010-09-17 15:12:57

JVMJava虛擬機(jī)

2024-03-12 07:44:53

JVM雙親委托機(jī)制類加載器

2012-03-01 10:51:37

JavaJVM

2010-09-25 15:59:54

JVM虛擬機(jī)

2020-05-08 16:55:48

Java虛擬機(jī)JVM

2011-06-22 13:35:55

JVM

2010-09-25 15:13:40

JVMJava虛擬機(jī)

2009-08-14 17:52:27

C#對(duì)象初始化

2011-12-28 13:38:00

JavaJVM

2011-12-28 13:24:47

JavaJVM

2011-11-30 14:12:05

JavaJVM虛擬機(jī)

2024-03-29 11:42:21

Java虛擬機(jī)

2019-07-24 16:04:47

Java虛擬機(jī)并發(fā)

2009-06-12 15:41:01

Java虛擬機(jī)

2020-05-12 22:24:44

JVM系統(tǒng)加載器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)