《深入了解Java虛擬機(jī)》筆記

最近在閱讀《深入理解JAVA虛擬機(jī)》,因?yàn)楸救擞浶暂^差,而且這類(lèi)書(shū)本身就比較難懂,更需要反復(fù)閱讀,所以采用的是慢讀+筆記的學(xué)習(xí)方式,一處看不懂會(huì)反復(fù)地再閱讀。

本書(shū)是基于jdk7,目前的最新版本是jdk1.9(2017.9)

jdk1.x一般指的是開(kāi)發(fā)版本,jdkX是真正改動(dòng)后的名字。

筆記是按照閱讀順序(目錄順序?)



第一部分:走進(jìn)JAVA

概念:

1、JVM(JAVA virtual machine)Java虛擬機(jī),提供了字節(jié)碼文件(.class)的運(yùn)行環(huán)境支持。JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī),是通過(guò)在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的。引入Java語(yǔ)言虛擬機(jī)后,Java語(yǔ)言在不同平臺(tái)上運(yùn)行時(shí)不需要重新編譯。

Java語(yǔ)言使用Java虛擬機(jī)屏蔽了與具體平臺(tái)相關(guān)的信息,使得Java語(yǔ)言編譯程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺(tái)上不加修改地運(yùn)行。Java虛擬機(jī)在執(zhí)行字節(jié)碼時(shí),把字節(jié)碼解釋成具體平臺(tái)上的機(jī)器指令執(zhí)行。這就是Java的能夠“一次編譯,到處運(yùn)行”的原因。

2、JRE(JAVA Runtime Environment):是支持JAVA程序運(yùn)行的標(biāo)準(zhǔn)環(huán)境,包含JAVA SE API子架,JAVA虛擬機(jī)。運(yùn)行JAVA程序所必須的環(huán)境的集合,包含JVM標(biāo)準(zhǔn)實(shí)現(xiàn)及Java核心類(lèi)庫(kù)。

.jre判斷程序是否執(zhí)行結(jié)束的標(biāo)準(zhǔn)時(shí):所有的前臺(tái)線程都執(zhí)行完畢。

3、JDK(Java Development Kit):是支持JAVA程序開(kāi)發(fā)的最小環(huán)境,是Java 語(yǔ)言的軟件開(kāi)發(fā)工具包(SDK)。包含JAVA語(yǔ)言、JAVA虛擬機(jī)、JAVA API類(lèi)庫(kù)。

jdk(java開(kāi)發(fā)工具)> jre(java運(yùn)行時(shí)環(huán)境)> jvm(java虛擬機(jī))

4、java一開(kāi)始是sun公司,后來(lái)的oracle公司收購(gòu)。

5、現(xiàn)在主流的虛擬機(jī)有:HotSpot VM


第二部分:自動(dòng)內(nèi)存管理機(jī)制

一、運(yùn)行時(shí)java虛擬機(jī)管理的數(shù)據(jù)區(qū)域:

java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)

1、程序計(jì)數(shù)器:該內(nèi)存區(qū)域是唯一一個(gè)在java虛擬機(jī)規(guī)范中沒(méi)規(guī)定任何OutOfMemoryError情況的區(qū)域。線程隔離。

每條線程有一個(gè)獨(dú)立的PC,且互不影響,獨(dú)立存儲(chǔ)。是線程私有的內(nèi)存。

用于選取下一條需要執(zhí)行的字節(jié)碼指令。

2、JAVA虛擬機(jī)棧:為虛擬機(jī)執(zhí)行java方法(也就是字節(jié)碼)服務(wù)

也是線程私有的內(nèi)存,生命周期同線程。是線程隔離的。

描述JAVA方法執(zhí)行的內(nèi)存模型,每個(gè)方法調(diào)用時(shí)都創(chuàng)建一個(gè)棧幀,用于存儲(chǔ)信息。方法從調(diào)用到執(zhí)行完成,意味一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧。

一般JAVA內(nèi)存區(qū)的棧就是指虛擬機(jī)棧。

在java虛擬機(jī)規(guī)范中:對(duì)該區(qū)域規(guī)定2種異常狀況:

(1)線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,拋出StackOverflowError異常;

(2)虛擬機(jī)棧擴(kuò)展時(shí)無(wú)法申請(qǐng)足夠內(nèi)存,拋出OutOfMemoryError異常

3、本地方法棧(為虛擬機(jī)使用到的native方法服務(wù))

可以自由實(shí)現(xiàn),或者有的JVM將虛擬機(jī)棧和方法棧合二為一。

在java虛擬機(jī)規(guī)范中:同樣拋出兩種異常。

棧區(qū):存方法局部變量

4、JAVA堆:所有線程共享的內(nèi)存區(qū)域

一般時(shí)JAVA虛擬機(jī)所管理的內(nèi)存中最大的一塊,被所有線程共享,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。

目的:存放對(duì)象實(shí)例(所有對(duì)象實(shí)例都在這分配內(nèi)存)類(lèi)對(duì)象!!

JAVA堆時(shí)垃圾收集器管理的主要區(qū)域(常叫為GC堆,Garbage Collected Heap)

在java虛擬機(jī)規(guī)范中:可處于物理上不連續(xù)的內(nèi)存空間,只要邏輯上是連續(xù)的即可。若在堆中沒(méi)有內(nèi)存完成實(shí)例分配,且堆也無(wú)法再擴(kuò)展,拋出OutOfMemoryError異常。

5、方法區(qū):所有線程共享的內(nèi)存區(qū)域,與堆一樣

用于存儲(chǔ)已被虛擬機(jī)加載的類(lèi)信息、常量、靜態(tài)變量、即時(shí)編譯器變異后的代碼等數(shù)據(jù)。

在java虛擬機(jī)規(guī)范中:

不需要連續(xù)的內(nèi)存和可選擇固定大小或者可擴(kuò)展

可選擇不是先垃圾收集(也常被成為永久代),但一般收集是必要的,不然會(huì)內(nèi)存泄漏...bug

當(dāng)方法區(qū)無(wú)法滿足內(nèi)存分配需求時(shí),拋出OutOfMemoryError異常

(1)運(yùn)行時(shí)常量池:是方法區(qū)一部分

存放編譯期生成的各種字面量和符號(hào)引用,將在類(lèi)加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。

沒(méi)有什么規(guī)定。

但具備動(dòng)態(tài)性,常量不一定只有編譯期產(chǎn)生。同樣,當(dāng)方法區(qū)無(wú)法滿足內(nèi)存分配需求時(shí),拋出OutOfMemoryError異常

PS:直接內(nèi)存:不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)

但常使用,也可能導(dǎo)致OutOfMemoryError異常


總結(jié)?。?!

方法區(qū):線程共享,類(lèi)信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)

(運(yùn)行時(shí)常量池:方法區(qū)一部分,存類(lèi)版本、字段、方法、接口等信息)

堆:線程共享,類(lèi)對(duì)象,new出來(lái)的對(duì)象。

棧:線程隔離,存放方法局部變量,對(duì)象引用。

程序計(jì)數(shù)器:線程隔離,指示當(dāng)前所執(zhí)行的字節(jié)碼行號(hào),唯一不拋異常。

```

String s="abc";? //兩個(gè)對(duì)象,引用s:棧中對(duì)象;“abc”:常量池對(duì)象

String? s=new String("abc"); //三個(gè)對(duì)象:引用s:棧中對(duì)象;“abc":常量池對(duì)象;new String:堆中對(duì)象

```



二、 HotSpot虛擬機(jī)對(duì)象

1、對(duì)象的創(chuàng)建:

通常是(1)new創(chuàng)建

(2)虛擬機(jī)先檢查這個(gè)指令的參數(shù)是否能在常量池中定位到一個(gè)類(lèi)的符號(hào)引用,并且檢查這個(gè)符號(hào)引用代表的類(lèi)是否被加載、解析和初始化過(guò),沒(méi)有則先執(zhí)行相應(yīng)的類(lèi)加載過(guò)程.

(3)為新生對(duì)象分配內(nèi)存(即把一塊確定大小的內(nèi)存從java堆中劃分出來(lái))(對(duì)象創(chuàng)建時(shí)并發(fā)情況可能線程不安全,解決方案:①對(duì)分配內(nèi)存空間的動(dòng)作進(jìn)行同步處理;? ②把內(nèi)存分配的動(dòng)作按照線程劃分在不同的空間中進(jìn)行,即每個(gè)線程在java堆中預(yù)先分配一小塊內(nèi)存,稱為本地線程分配緩沖 TLAB)?

(4)內(nèi)存分配完成后,虛擬機(jī)將分配到的內(nèi)存空間都初始化為零值。

(5)虛擬機(jī)對(duì)對(duì)象進(jìn)行必要的設(shè)置

2、對(duì)象的內(nèi)存布局:

HotSpot虛擬機(jī)中,對(duì)象在內(nèi)存中存儲(chǔ)的布局分為3塊區(qū)域:對(duì)象頭(Header)、實(shí)例數(shù)據(jù)(Instance Data)、對(duì)齊填充(Padding)

(1)虛擬機(jī)對(duì)象頭包括兩部分信息:

第一部分用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù)(通常長(zhǎng)度是32位或64位,對(duì)32位的,存儲(chǔ)對(duì)象哈希碼25bit,對(duì)象分代年齡4bit,鎖標(biāo)志位2bit,1bit固定為0);是8字節(jié)整數(shù)倍

第二部分是類(lèi)型指針,即對(duì)象指向它的類(lèi)元數(shù)據(jù)的指針,虛擬機(jī)通過(guò)這個(gè)指針確定這個(gè)對(duì)象是哪個(gè)類(lèi)的實(shí)例。但并不是所有虛擬機(jī)都保存類(lèi)型指針。

(2)實(shí)例數(shù)據(jù):對(duì)象真正存儲(chǔ)的有效信息,代碼中所定義的各種類(lèi)型的字段內(nèi)容,分配策略是相同狂賭字段分配一起(ints、longs/doubles、shorts/chars、bytes/booleans、oops)

(3)對(duì)齊填充:不是必須,用于補(bǔ)全8字節(jié)的整數(shù)倍

3、對(duì)象的訪問(wèn)定位:

java程序通過(guò)棧上的reference數(shù)據(jù)操作堆上的具體對(duì)象(reference類(lèi)型在java虛擬機(jī)規(guī)范中只規(guī)定了一個(gè)指向?qū)ο蟮囊茫?/p>

訪問(wèn)方式:

1、使用句柄訪問(wèn):劃分一塊內(nèi)存做句柄池,reference存對(duì)象的句柄地址(穩(wěn)定的句柄地址)

2、直接指針訪問(wèn):reference存對(duì)象地址(較快)


三、OutOfMemoryError異常:

1、堆溢出:OutOfMemoryError + "java heap space"

處理方式:

首先確認(rèn)是內(nèi)存中的對(duì)象是否是必要的?

內(nèi)存泄漏(Memory Leak):準(zhǔn)確定位泄漏代碼位置

內(nèi)存溢出(Memory Overflow):內(nèi)存中的對(duì)象確實(shí)還必須存活,應(yīng)檢查虛擬機(jī)的堆參數(shù)(-Xmx,-Xms),減少內(nèi)存消耗、調(diào)大堆參數(shù)

2、虛擬機(jī)棧和本地方法棧溢出:

java虛擬機(jī)規(guī)范中兩種異常:

(1)線程請(qǐng)求的棧深度? > 虛擬機(jī)所允許的最大深度:StackOverflowError異常

(2)虛擬機(jī)在擴(kuò)展棧時(shí)無(wú)法申請(qǐng)到足夠內(nèi)存空間:OutOfMemoryError異常

兩者可能重疊

3、方法區(qū)和運(yùn)行時(shí)常量池溢出:

運(yùn)行時(shí)常量池是方法區(qū)的一部分。

String.intern():Native方法,若字符串常量池已經(jīng)包含一個(gè)等于此String對(duì)象的字符串,則返回代表池中這個(gè)字符串的String對(duì)象,否則,將此String對(duì)象包含的字符串添加到常量池中,并返回此String對(duì)象的引用。

4、本機(jī)直接內(nèi)存溢出


第三部分:垃圾收集器與內(nèi)存分配策略

GC:Garbage Collection

一、判定對(duì)象是否存活的算法:垃圾收集器對(duì)堆進(jìn)行回收前,要先確定這些對(duì)象中哪些還存活,哪些已經(jīng)死去(不可能再被任何途徑使用的對(duì)象)

【JVM中共劃分為三個(gè)代:年輕代、年老代和持久代,

年輕代:存放所有新生成的對(duì)象;

年老代:在年輕代中經(jīng)歷了N次垃圾回收仍然存活的對(duì)象,將被放到年老代中,故都是一些生命周期較長(zhǎng)的對(duì)象;

持久代:用于存放靜態(tài)文件,如Java類(lèi)、方法等。

新生代的垃圾收集器命名為“minor gc”,老生代的GC命名為”Full Gc 或者M(jìn)ajor GC”.其中用System.gc()強(qiáng)制執(zhí)行的是Full Gc.】

判斷對(duì)象是否需要回收的辦法?

【1、引用計(jì)數(shù)算法:

給對(duì)象添加一個(gè)引用計(jì)數(shù)器,每當(dāng)一個(gè)地方引用它,計(jì)數(shù)器值加一;當(dāng)引用失效時(shí),計(jì)數(shù)器值減一;任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不可能再被使用的。(python用了,但java虛擬機(jī)沒(méi)用引用計(jì)數(shù)算法來(lái)管理內(nèi)存,因?yàn)楹茈y解決對(duì)象間相互循環(huán)引用的問(wèn)題)

2、可達(dá)性分析算法:

通過(guò)一系列的被稱為“GC Roots”的對(duì)象作為七十點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索,所走過(guò)的路徑稱為引用鏈,當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈(從GC Roots到該對(duì)象不可達(dá)),則對(duì)象不可用,可回收?!?/p>

GC Roots包括:

虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象、方法區(qū)中類(lèi)靜態(tài)屬性引用的對(duì)象、方法區(qū)常量引用的對(duì)象、本地方法棧中JNI(即Native方法)引用的對(duì)象



3、引用:

強(qiáng)引用:Object obj=new Object(),new的對(duì)象,強(qiáng)引用存在,其被引用的對(duì)象就不會(huì)被回收。

軟引用:描述還有用但非必需的對(duì)象。系統(tǒng)將發(fā)生內(nèi)存溢出異常之前回收,如果還沒(méi)有足夠內(nèi)存,才拋出內(nèi)存溢出異常。

弱引用:描述非必需對(duì)象。其關(guān)聯(lián)對(duì)象只能生存到下一次垃圾收集發(fā)生之前。

虛引用(幽靈引用/幻影引用):一定會(huì)被回收,回收前會(huì)通知


二、判定不存活的對(duì)象,至少進(jìn)行兩次標(biāo)記,才確定真正死亡。

1、可達(dá)性分析發(fā)現(xiàn)不可達(dá),則進(jìn)行第一次標(biāo)記+一次篩選

2、篩選:對(duì)象是否有必要執(zhí)行finalize()方法(只能執(zhí)行一次

“沒(méi)有必要執(zhí)行”——對(duì)象沒(méi)有覆蓋finalize()方法? 或? finalize()方法已被虛擬機(jī)調(diào)用過(guò)

“有必要執(zhí)行”——F-Queue隊(duì)列,若對(duì)象在finalize()方法中重新與引用鏈上的任何一個(gè)對(duì)象建立關(guān)聯(lián),則第二次標(biāo)記時(shí)會(huì)被移除出"即將回收"集合,成功拯救。否則——被回收。

但finalize()方法不鼓勵(lì)用于拯救對(duì)象。

3、回收方法區(qū):

java不要求虛擬機(jī)在方法區(qū)實(shí)現(xiàn)垃圾收集,且收集效率也較低

永久代垃圾收集回收:廢棄常量、無(wú)用的類(lèi)。

(1)廢棄常量:

一個(gè)進(jìn)入常量池的常量,不再被引用,則廢棄,被清理出常量池

(2)無(wú)用的類(lèi):

同時(shí)滿足:該類(lèi)所有的實(shí)例都已被回收(java堆中不存在該類(lèi)任何實(shí)例)、加載該類(lèi)的ClassLoader已被回收、該類(lèi)對(duì)應(yīng)的java.lang,Class對(duì)象沒(méi)在任何地方被引用(無(wú)法在任何地方通過(guò)反射訪問(wèn)該類(lèi)的方法)

則該類(lèi)可回收,但不是必然回收。


三、垃圾收集算法:

1、標(biāo)記-清除算法:標(biāo)記所有需要回收的對(duì)象,標(biāo)記完成后統(tǒng)一回收。(效率不高;清除后產(chǎn)生大量不連續(xù)的內(nèi)存碎片)

2、復(fù)制算法(很多用):把可用內(nèi)存分兩塊,每次只使用其中一塊;當(dāng)這塊內(nèi)存用完了,將還存活的對(duì)象復(fù)制到另一塊上,將已使用的一次清除(實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效;但每次將內(nèi)存縮小到一半)

3、標(biāo)記-整理算法(常用于老年代):標(biāo)記所有需要回收的對(duì)象,將還存活的對(duì)象向一端移動(dòng),直接清理掉端邊界以外的內(nèi)存。

4、分代收集算法:新生代:復(fù)制算法;老年代:標(biāo)記-清除/標(biāo)記-整理


五、各類(lèi)垃圾收集器(基于HotSpot虛擬機(jī))

垃圾收集器是內(nèi)存回收的具體體現(xiàn)。

1、Serial收集器:Client模式下的新生代收集器,單線程

2、Serial Old收集器:Client模式下的老年代收集器

(1、2是串行收集器)

3、ParNew收集器:server模式下的首選新生代收集器(Serial收集器的多線程版本)

4、Parall Scavenge收集器:更加關(guān)注吞吐量

5、Parall? Old收集器:parall scavenge收集器的老年代版本

(3、4、5是并行收集器)

6、G1收集器:整體上看,標(biāo)記-整理,部分看是復(fù)制;新生代老年代都有,更加關(guān)注停頓時(shí)間。面向服務(wù)端應(yīng)用。初始標(biāo)記;并發(fā)標(biāo)記;最終標(biāo)記;篩選回收。

7、CMS收集器:并發(fā)標(biāo)記清除,獲取最短回收停頓時(shí)間為目的,老年代收集器(大量空間碎片),分為4個(gè)步驟:初始標(biāo)記、并發(fā)標(biāo)記、重新標(biāo)記、并發(fā)清除。

【總結(jié):

串行收集器:?jiǎn)尉€程,暫停所有應(yīng)用線程來(lái)工作

并行收集器:默認(rèn)的垃圾收集器。多線程。

G1收集器:新生代,老年代都有;整體(主要)是標(biāo)記-整理,部分是復(fù)制算法。面向服務(wù)端應(yīng)用。初始標(biāo)記、并發(fā)標(biāo)記、最終標(biāo)記、篩選回收。

CMS收集器:以獲取最短回收停頓時(shí)間為目標(biāo),老年代收集器;并發(fā)標(biāo)記-清除。分為4個(gè)步驟:初始標(biāo)記、并發(fā)標(biāo)記、重新標(biāo)記、并發(fā)清除。


第七章? 虛擬機(jī)類(lèi)加載機(jī)制

一、類(lèi)加載機(jī)制:虛擬機(jī)把描述類(lèi)的數(shù)據(jù)從Class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類(lèi)型

類(lèi)型的加載、連接和初始化都是在程序運(yùn)行期間完成的。(此處的Class文件更多表示一個(gè)類(lèi)或接口,是一串二進(jìn)制字節(jié)流。)


二、類(lèi)的生命周期:加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用、卸載? 7階段(連接:驗(yàn)證-->準(zhǔn)備-->初始化)

類(lèi)加載的五個(gè)過(guò)程:加載、驗(yàn)證、準(zhǔn)備、解析、初始化。


(a)加載:類(lèi)的全限定名--->定義此類(lèi)的二進(jìn)制字節(jié)流--->由字節(jié)流的靜態(tài)存儲(chǔ)結(jié)構(gòu)--->轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)--->內(nèi)存中生成代表該類(lèi)的java.lang.Class對(duì)象(方法區(qū)這個(gè)類(lèi)的各種數(shù)據(jù)的訪問(wèn)入口)

(1)通過(guò)類(lèi)的全限定名來(lái)獲取定義此類(lèi)的二進(jìn)制字節(jié)流(從zip包獲取——jar,war,ear;從網(wǎng)絡(luò)中獲取——Applet;運(yùn)行時(shí)計(jì)算生成——?jiǎng)討B(tài)代理;由其他文件生成——JSP應(yīng)用;從數(shù)據(jù)庫(kù)中獲取)

(2)由字節(jié)流的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)

(3)在內(nèi)存中生成代表該類(lèi)的java.lang.Class對(duì)象,作為方法去這個(gè)類(lèi)的各種數(shù)據(jù)的訪問(wèn)入口

加載有兩種情況,①當(dāng)遇到new關(guān)鍵字或static關(guān)鍵字的時(shí)候就會(huì)發(fā)生

②動(dòng)態(tài)加載,當(dāng)用反射方法(如class.forName(“類(lèi)名”)),如果發(fā)現(xiàn)沒(méi)有初始化,則要進(jìn)行初始化。(注:加載的時(shí)候發(fā)現(xiàn)父類(lèi)沒(méi)有被加載,則要先加載父類(lèi))

加載階段可使用系統(tǒng)的引導(dǎo)類(lèi)加載器完成,或用戶自定義的類(lèi)加載器完成(自定義控制字節(jié)流的獲取方式),加載和連接階段交錯(cuò)進(jìn)行。

(b)驗(yàn)證:連接階段第一步

這一階段的目的是確保class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并不會(huì)危害虛擬機(jī)自身的安全。

驗(yàn)證階段4個(gè)階段檢驗(yàn)動(dòng)作:【文件格式檢驗(yàn)、元數(shù)據(jù)驗(yàn)證(語(yǔ)義校驗(yàn))、字節(jié)碼驗(yàn)證、符號(hào)引用驗(yàn)證(解析時(shí)發(fā)生)】

(c)準(zhǔn)備:

正式為類(lèi)變量分配內(nèi)存并設(shè)置類(lèi)變量初始值,內(nèi)存分配在方法區(qū)。

注意:!!此時(shí)只對(duì)類(lèi)變量(static修飾的)進(jìn)行內(nèi)存分配,不包括實(shí)例變量(在對(duì)象實(shí)例化時(shí)隨對(duì)象一起分配在java堆)。!!此時(shí)進(jìn)行的是默認(rèn)初始化,賦零值,即=0,=false;

(d)解析:虛擬機(jī)將常量池中的符號(hào)引用替換成直接引用的過(guò)程。

符號(hào)引用:以一組符號(hào)來(lái)描述所引用的目標(biāo)(不一定已加載到內(nèi)存中)

直接引用:直接指向目標(biāo)的指針、相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄。(目標(biāo)必定加載到了內(nèi)存)


(e) 初始化:真正執(zhí)行類(lèi)中的代碼

是執(zhí)行類(lèi)構(gòu)造器<clinit>()方法的過(guò)程。

用于:創(chuàng)建類(lèi)的實(shí)例、訪問(wèn)類(lèi)或接口的靜態(tài)變量、調(diào)用類(lèi)的靜態(tài)方法、反射(Class.forName())、初始化類(lèi)的子類(lèi)

【<clinit>方法:編譯器自動(dòng)收集類(lèi)中的所有類(lèi)變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊中的語(yǔ)句合并產(chǎn)生;JVM會(huì)保證父類(lèi)的<clinit>()先執(zhí)行,所以第一個(gè)執(zhí)行的一定是Object類(lèi),所以父類(lèi)中定義的靜態(tài)語(yǔ)句塊優(yōu)先于子類(lèi)的變量賦值操作;該方法對(duì)類(lèi)/接口不是必須的,如果類(lèi)無(wú)靜態(tài)語(yǔ)句塊/對(duì)變量的賦值動(dòng)作,可無(wú)該方法?!?/p>


(三)類(lèi)加載器:完成加載階段的【通過(guò)一個(gè)類(lèi)的全限定名來(lái)獲取描述此類(lèi)的二進(jìn)制字節(jié)流】的操作。在JVM外部實(shí)現(xiàn)。

1、分類(lèi):

(1)從JVM來(lái)看,

a.啟動(dòng)類(lèi)加載器(C++,是虛擬機(jī)自身的一部分)

b.所有其他類(lèi)加載器(Java,獨(dú)立于虛擬機(jī)外部,全都繼承自抽象類(lèi)java.lang.ClassLoader)

(2)從開(kāi)發(fā)來(lái)看,

a.啟動(dòng)類(lèi)加載器:Bootstrap? ClassLoader:

負(fù)責(zé)加載 存放在<JAVA_HOME>\lib目錄中,或被-Xbootclasspath參數(shù)所指定的路徑中,被虛擬機(jī)識(shí)別的類(lèi)庫(kù)? 到 虛擬機(jī)內(nèi)存中。

b.擴(kuò)展類(lèi)加載器:Extension ClassLoader,可直接使用,負(fù)責(zé)加載? ?<JAVA_HOME>\lib\ext目錄中,或被java.ext.dirs系統(tǒng)變量所指定的路徑中的所有類(lèi)庫(kù)? ?到? ?虛擬機(jī)內(nèi)存中。

c.應(yīng)用程序類(lèi)加載器:可直接使用,負(fù)責(zé)加載? 用戶類(lèi)路徑(ClassPath)上所指定的類(lèi)庫(kù)。是程序默認(rèn)的類(lèi)加載器。

d.自定義類(lèi)加載器:通過(guò)繼承ClassLoader實(shí)現(xiàn),一般是加載我們的自定義類(lèi)

2、類(lèi)加載器間層次關(guān)系:

a.雙親委派模型:組合關(guān)系。

請(qǐng)求委派給父類(lèi),最終都傳到啟動(dòng)類(lèi)加載器,只有父類(lèi)加載器反饋?zhàn)约簾o(wú)法加載,子加載器才嘗試加載。

其代碼都集中在java.lang.ClassLoader的loadClass()方法中?!鞠葯z查是否被加載過(guò),無(wú)則調(diào)用父類(lèi)的loadClass(),父加載器為空則使用啟動(dòng)類(lèi)加載器作為父加載器,若父類(lèi)加載失敗,拋出ClassNotFoundException,調(diào)用自己的findClass()加載】

【阿里的面試官問(wèn)我,可以不可以自己寫(xiě)個(gè)String類(lèi)

答案:不可以,因?yàn)?根據(jù)類(lèi)加載的雙親委派機(jī)制,會(huì)去加載父類(lèi),父類(lèi)發(fā)現(xiàn)沖突了String就不再加載了;

可以類(lèi)比到其他已經(jīng)有的類(lèi)。我試過(guò),然后其他同個(gè)包中的所有類(lèi)有運(yùn)用到原先的String類(lèi)的全都報(bào)錯(cuò),因?yàn)橐玫搅诉@個(gè)類(lèi)?!?/p>





說(shuō)一說(shuō)你對(duì)環(huán)境變量classpath的理解?如果一個(gè)類(lèi)不在classpath下,為什么會(huì)拋出ClassNotFoundException異常,如果在不改變這個(gè)類(lèi)路徑的前期下,怎樣才能正確加載這個(gè)類(lèi)?

classpath是javac編譯器的一個(gè)環(huán)境變量。它的作用與import、package關(guān)鍵字有關(guān)。package的所在位置,就是設(shè)置CLASSPATH當(dāng)編譯器面對(duì)import packag這個(gè)語(yǔ)句時(shí),它先會(huì)查找CLASSPATH所指定的目錄,并檢視子目錄java/util是否存在,然后找出名稱吻合的已編譯文件(.class文件)。如果沒(méi)有找到就會(huì)報(bào)錯(cuò)!

動(dòng)態(tài)加載包

-

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容