主頁 > 後端開發 > JVM初步了解(轉)

JVM初步了解(轉)

2020-10-05 20:23:31 後端開發

Java運行時資料區:Java虛擬機在執行Java程式的程序中會將其管理的記憶體劃分為若干個不同的資料區域,這些區域有各自的用途、創建和銷毀的時間,有些區域隨虛擬機行程的啟動而存在,有些區域則是依賴用戶執行緒的啟動和結束來建立和銷毀,Java虛擬機所管理的記憶體包括以下幾個運行時資料區域,如圖:

 

 

 

 

1、程式計數器:指向當前執行緒正在執行的位元組碼指令,執行緒私有的,

2、虛擬機堆疊:虛擬機堆疊是Java執行方法的記憶體模型,每個方法被執行的時候,都會創建一個堆疊幀,把堆疊幀壓人堆疊,當方法正常回傳或者拋出未捕獲的例外時,堆疊幀就會出堆疊,

(1)堆疊幀:堆疊幀存盤方法的相關資訊,包含區域變數數表、回傳值、運算元堆疊、動態鏈接

a、區域變數表:包含了方法執行程序中的所有變數,區域變數陣列所需要的空間在編譯期間完成分配,在方法運行期間不會改變區域變數陣列的大小,

b、回傳值:如果有回傳值的話,壓入呼叫者堆疊幀中的運算元堆疊中,并且把PC的值指向 方法呼叫指令 后面的一條指令地址,

c、運算元堆疊:操作變數的記憶體模型,運算元堆疊的最大深度在編譯的時候已經確定(寫入方法區code屬性的max_stacks項中),運算元堆疊的的元素可以是任意Java型別,包括long和double,32位資料占用堆疊空間為1,64位資料占用2,方法剛開始執行的時候,堆疊是空的,當方法執行程序中,各種位元組碼指令往堆疊中存取資料,

d、動態鏈接:每個堆疊幀都持有在運行時常量池中該堆疊幀所屬方法的參考,持有這個參考是為了支持方法呼叫程序中的動態鏈接,

(2)執行緒私有

3、本地方法堆疊:

(1)呼叫本地native的記憶體模型

(2)執行緒獨享,

4、方法區:用于存盤已被虛擬機加載的類資訊、常量、靜態變數、即時編譯后的代碼等資料

(1)執行緒共享的

(2)運行時常量池:A、是方法區的一部分
B、存放編譯期生成的各種字面量和符號參考
C、Class檔案中除了存有類的版本、欄位、方法、介面等描述資訊,還有一項是常量池,存有這個類的 編譯期生成的各種字面量和符號參考,這部分內容將在類加載后,存放到方法區的運行時常量池中,

5、堆(Heap):Java物件存盤的地方

(1)Java堆是虛擬機管理的記憶體中最大的一塊

(2)Java堆是所有執行緒共享的區域

(3)在虛擬機啟動時創建

(4)此記憶體區域的唯一目的就是存放物件實體,幾乎所有物件實體都在這里分配記憶體,存放new生成的物件和陣列

(5)Java堆是垃圾收集器管理的記憶體區域,因此很多時候稱為“GC堆”

JMM Java記憶體模型:

1、 Java的并發采用“共享記憶體”模型,執行緒之間通過讀寫記憶體的公共狀態進行通訊,多個執行緒之間是不能通過直接傳遞資料互動的,它們之間互動只能通過共享變數實作,

2、 主要目的是定義程式中各個變數的訪問規則,

3、 Java記憶體模型規定所有變數都存盤在主記憶體中,每個執行緒還有自己的作業記憶體,

(1) 執行緒的作業記憶體中保存了被該執行緒使用到的變數的拷貝(從主記憶體中拷貝過來),執行緒對變數的所有操作都必須在作業記憶體中執行,而不能直接訪問主記憶體中的變數,

(2) 不同執行緒之間無法直接訪問對方作業記憶體的變數,執行緒間變數值的傳遞都要通過主記憶體來完成,

(3) 主記憶體主要對應Java堆中實體資料部分,作業記憶體對應于虛擬機堆疊中部磁區域,

 

 

 

4、Java執行緒之間的通信由記憶體模型JMM(Java Memory Model)控制,

(1)JMM決定一個執行緒對變數的寫入何時對另一個執行緒可見,

(2)執行緒之間共享變數存盤在主記憶體中

(3)每個執行緒有一個私有的本地記憶體,里面存盤了讀/寫共享變數的副本,

(4)JMM通過控制每個執行緒的本地記憶體之間的互動,來為程式員提供記憶體可見性保證,

5、可見性、有序性:

(1)當一個共享變數在多個本地記憶體中有副本時,如果一個本地記憶體修改了該變數的副本,其他變數應該能夠看到修改后的值,此為可見性,

(2)保證執行緒的有序執行,這個為有序性,(保證執行緒安全)

6、記憶體間互動操作:

(1)lock(鎖定):作用于主記憶體的變數,把一個變數標識為一條執行緒獨占狀態,

(2)unlock(解鎖):作用于主記憶體的變數,把一個處于鎖定狀態的變數釋放出來,釋放后的變數才可以被其他執行緒鎖定,

(3)read(讀取):作用于主記憶體變數,把主記憶體的一個變數讀取到作業記憶體中,

(4)load(載入):作用于作業記憶體,把read操作讀取到作業記憶體的變數載入到作業記憶體的變數副本中

(5)use(使用):作用于作業記憶體的變數,把作業記憶體中的變數值傳遞給一個執行引擎,

(6)assign(賦值):作用于作業記憶體的變數,把執行引擎接收到的值賦值給作業記憶體的變數,

(7)store(存盤):把作業記憶體的變數的值傳遞給主記憶體

(8)write(寫入):把store操作的值入到主記憶體的變數中

6.1、注意:

(1)不允許read、load、store、write操作之一單獨出現

(2)不允許一個執行緒丟棄assgin操作

(3)不允許一個執行緒不經過assgin操作,就把作業記憶體中的值同步到主記憶體中

(4)一個新的變數只能在主記憶體中生成

(5)一個變數同一時刻只允許一條執行緒對其進行lock操作,但lock操作可以被同一條執行緒執行多次,只有執行相同次數的unlock操作,變數才會解鎖

(6)如果對一個變數進行lock操作,將會清空作業記憶體中此變數的值,在執行引擎使用這個變數前,需要重新執行load或者assgin操作初始化變數的值,

(7)如果一個變數沒有被鎖定,不允許對其執行unlock操作,也不允許unlock一個被其他執行緒鎖定的變數

(8)對一個變數執行unlock操作之前,需要將該變數同步回主記憶體中

堆的記憶體劃分:

 

 

Java堆的記憶體劃分如圖所示,分別為年輕代、Old Memory(老年代)、Perm(永久代),其中在Jdk1.8中,永久代被移除,使用MetaSpace代替,

1、新生代:

(1)使用復制清除演算法(Copinng演算法),原因是年輕代每次GC都要回收大部分物件,新生代里面分成一份較大的Eden空間和兩份較小的Survivor空間,每次只使用Eden和其中一塊Survivor空間,然后垃圾回收的時候,把存活物件放到未使用的Survivor(劃分出from、to)空間中,清空Eden和剛才使用過的Survivor空間,

(2)分為Eden、Survivor From、Survivor To,比例默認為8:1:1

(3)記憶體不足時發生Minor GC

2、老年代:

(1)采用標記-整理演算法(mark-compact),原因是老年代每次GC只會回收少部分物件,

3、Perm:用來存盤類的元資料,也就是方法區,

(1)Perm的廢除:在jdk1.8中,Perm被替換成MetaSpace,MetaSpace存放在本地記憶體中,原因是永久代進場記憶體不夠用,或者發生記憶體泄漏,

(2)MetaSpace(元空間):元空間的本質和永久代類似,都是對JVM規范中方法區的實作,不過元空間與永久代之間最大的區別在于:元空間并不在虛擬機中,而是使用本地記憶體,

4、堆記憶體的劃分在JVM里面的示意圖:

 

 

GC垃圾回收:

一、 判斷物件是否要回收的方法:可達性分析法

1、 可達性分析法:通過一系列“GC Roots”物件作為起點進行搜索,如果在“GC Roots”和一個物件之間沒有可達路徑,則稱該物件是不可達的,不可達物件不一定會成為可回收物件,進入DEAD狀態的執行緒還可以恢復,GC不會回收它的記憶體,(把一些物件當做root物件,JVM認為root物件是不可回收的,并且root物件參考的物件也是不可回收的)

2、 以下物件會被認為是root物件:

(1) 虛擬機堆疊(堆疊幀中本地變數表)中參考的物件

(2) 方法區中靜態屬性參考的物件

(3) 方法區中常量參考的物件

(4) 本地方法堆疊中Native方法參考的物件

3、 物件被判定可被回收,需要經歷兩個階段:

(1) 第一個階段是可達性分析,分析該物件是否可達

(2) 第二個階段是當物件沒有重寫finalize()方法或者finalize()方法已經被呼叫過,虛擬機認為該物件不可以被救活,因此回收該物件,(finalize()方法在垃圾回收中的作用是,給該物件一次救活的機會)

4、 方法區中的垃圾回收:

(1) 常量池中一些常量、符號參考沒有被參考,則會被清理出常量池

(2) 無用的類:被判定為無用的類,會被清理出方法區,判定方法如下:

A、 該類的所有實體被回收

B、 加載該類的ClassLoader被回收

C、 該類的Class物件沒有被參考

5、 finalize():

(1) GC垃圾回收要回收一個物件的時候,呼叫該物件的finalize()方法,然后在下一次垃圾回收的時候,才去回收這個物件的記憶體,

(2) 可以在該方法里面,指定一些物件在釋放前必須執行的操作,

二、 發現虛擬機頻繁full GC時應該怎么辦:(full GC指的是清理整個堆空間,包括年輕代和永久代)

(1) 首先用命令查看觸發GC的原因是什么 jstat –gccause 行程id

(2) 如果是System.gc(),則看下代碼哪里呼叫了這個方法

(3) 如果是heap inspection(記憶體檢查),可能是哪里執行jmap –histo[:live]命令

(4) 如果是GC locker,可能是程式依賴的JNI庫的原因

三、常見的垃圾回收演算法:

1、Mark-Sweep(標記-清除演算法):

(1)思想:標記清除演算法分為兩個階段,標記階段和清除階段,標記階段任務是標記出所有需要回收的物件,清除階段就是清除被標記物件的空間,

(2)優缺點:實作簡單,容易產生記憶體碎片

2、Copying(復制清除演算法):

(1)思想:將可用記憶體劃分為大小相等的兩塊,每次只使用其中的一塊,當進行垃圾回收的時候了,把其中存活物件全部復制到另外一塊中,然后把已使用的記憶體空間一次清空掉,

(2)優缺點:不容易產生記憶體碎片;可用記憶體空間少;存活物件多的話,效率低下,

3、Mark-Compact(標記-整理演算法):

(1)思想:先標記存活物件,然后把存活物件向一邊移動,然后清理掉端邊界以外的記憶體,

(2)優缺點:不容易產生記憶體碎片;記憶體利用率高;存活物件多并且分散的時候,移動次數多,效率低下

4、分代收集演算法:(目前大部分JVM的垃圾收集器所采用的演算法):

思想:把堆分成新生代和老年代,(永久代指的是方法區)

 

(1) 因為新生代每次垃圾回收都要回收大部分物件,所以新生代采用Copying演算法,新生代里面分成一份較大的Eden空間和兩份較小的Survivor空間,每次只使用Eden和其中一塊Survivor空間,然后垃圾回收的時候,把存活物件放到未使用的Survivor(劃分出from、to)空間中,清空Eden和剛才使用過的Survivor空間,

(2) 由于老年代每次只回收少量的物件,因此采用mark-compact演算法,

(3) 在堆區外有一個永久代,對永久代的回收主要是無效的類和常量

5、GC使用時對程式的影響?垃圾回識訓影響程式的性能,Java虛擬機必須要追蹤運行程式中的有用物件,然后釋放沒用物件,這個程序消耗處理器時間

6、幾種不同的垃圾回收型別:

(1)Minor GC:從年輕代(包括Eden、Survivor區)回收記憶體,

A、當JVM無法為一個新的物件分配記憶體的時候,越容易觸發Minor GC,所以分配率越高,記憶體越來越少,越頻繁執行Minor GC
B、執行Minor GC操作的時候,不會影響到永久代(Tenured),從永久代到年輕代的參考,被當成GC Roots,從年輕代到老年代的參考在標記階段直接被忽略掉,

(2)Major GC:清理整個老年代,當eden區記憶體不足時觸發,(3)Full GC:清理整個堆空間,包括年輕代和老年代,當老年代記憶體不足時觸發

HotSpot 虛擬機詳解:

1、 Java物件創建程序:

(1)虛擬機遇到一條new指令時,首先檢查這個指令的引數能否在常量池中定位到一個類的符號參考,并檢查這個符號參考代表的類是否已經加載、連接和初始化,如果沒有,就執行該類的加載程序,

(2)為該物件分配記憶體,

A、假設Java堆是規整的,所有用過的記憶體放在一邊,空閑的記憶體放在另外一邊,中間放著一個指標作為分界點的指示器,那分配記憶體只是把指標向空閑空間那邊挪動與物件大小相等的距離,這種分配稱為“指標碰撞”

B、假設Java堆不是規整的,用過的記憶體和空閑的記憶體相互交錯,那就沒辦法進行“指標碰撞”,虛擬機通過維護一個串列,記錄哪些記憶體塊是可用的,在分配的時候找出一塊足夠大的空間分配給物件實體,并更新表上的記錄,這種分配方式稱為“空閑串列“,

C、使用哪種分配方式由Java堆是否規整決定,Java堆是否規整由所采用的垃圾收集器是否帶有壓縮整理功能決定,

D、分配物件保證執行緒安全的做法:虛擬機使用CAS失敗重試的方式保證更新操作的原子性,(實際上還有另外一種方案:每個執行緒在Java堆中預先分配一小塊記憶體,稱為本地執行緒分配緩沖,TLAB,哪個執行緒要分配記憶體,就在哪個執行緒的TLAB上分配,只有TLAB用完并分配新的TLAB時,才進行同步鎖定,虛擬機是否使用TLAB,由-XX:+/-UseTLAB引數決定)

(3)虛擬機為分配的記憶體空間初始化為零值(默認值)

(4)虛擬機對物件進行必要的設定,例如這個物件是哪個類的實體、如何才能找到物件的元資料資訊、物件的Hash碼、物件的GC分代年齡等資訊,這些資訊存放在物件的物件頭中,

(5) 執行方法,把物件按照程式員的意愿進行初始化,

2、 物件的定位訪問的方式(通過參考如何去定位到堆上的具體物件的位置):

(1)句柄:使用句柄的方式,Java堆中將會劃分出一塊記憶體作為作為句柄池,參考中存盤的就是物件的句柄的地址,而句柄中包含了物件實體資料和物件型別資料的地址,

 

 

(2)直接指標:使用直接指標的方式,參考中存盤的就是物件的地址,Java堆物件的布局必須必須考慮如何去訪問物件型別資料

 

 

(3)兩種方式各有優點:

A、使用句柄訪問的好處是參考中存放的是穩定的句柄地址,當物件被移動(比如說垃圾回收時移動物件),只會改變句柄中實體資料指標,而參考本身不會被修改,

B、使用直接指標,節省了一次指標定位的時間開銷,

3、HotSpot的GC演算法實作:

(1)HotSpot怎么快速找到GC Root?HotSpot使用一組稱為OopMap的資料結構,在類加載完成的時候,HotSpot就把物件內什么偏移量上是什么型別的資料計算出來,在JIT編譯程序中,也會在堆疊和暫存器中哪些位置是參考,這樣子,在GC掃描的時候,就可以直接知道哪些是可達物件了,

(2)安全點:

A、HotSpot只在特定的位置生成OopMap,這些位置稱為安全點,

B、程式執行程序中并非所有地方都可以停下來開始GC,只有在到達安全點是才可以暫停,

C、安全點的選定基本上以“是否具有讓程式長時間執行“的特征選定的,比如說方法呼叫、回圈跳轉、例外跳轉等,具有這些功能的指令才會產生Safepoint,

(3)中斷方式:

A、搶占式中斷:在GC發生時,首先把所有執行緒中斷,如果發現有執行緒不在安全點上,就恢復執行緒,讓它跑到安全點上,
B、主動式中斷:GC需要中斷執行緒時,不直接對執行緒操作,僅僅設定一個標志,各個執行緒執行時主動去輪詢這個標志,當發現中斷標記為真就自己中斷掛起,輪詢標記的地方和安全點是重合的,

(5)安全區域:一段代碼片段中,物件的參考關系不會發生變化,在這個區域中任何地方開始GC都是安全的,在執行緒進入安全區域時,它首先標志自己已經進入安全區域,在這段時間里,當JVM發起GC時,就不用管進入安全區域的執行緒了,在執行緒將要離開安全區域時,它檢查系統是否完成了GC程序,如果完成了,它就繼續前行,否則,它就必須等待直到收到可以離開安全區域的信號,

4、 GC時為什么要停頓所有Java執行緒?因為GC先進行可達性分析,可達性分析是判斷GC Root物件到其他物件是否可達,假如分析程序中物件的參考關系在不斷變化,分析結果的準確性就無法得到保證,

5、 CMS收集器:

(1)一種以獲取最短回收停頓時間為目標的收集器,

(2)一般用于互聯網站或者B/S系統的服務端

(3)基于標記-清除演算法的實作,不過更為復雜,整個程序為4個步驟:

A、初始標記:標記GC Root能直接參考的物件
B、并發標記:利用多執行緒對每個GC Root物件進行tracing搜索,在堆中查找其下所有能關聯到的物件,
C、重新標記:為了修正并發標記期間,用戶程式繼續運作而導致標志產生變動的那一部分物件的標記記錄,
D、并發清除:利用多個執行緒對標記的物件進行清除

(4)由于耗時最長的并發標記和并發清除操作都是用戶執行緒一起作業,所以總體來說,CMS的記憶體回收作業是和用戶執行緒一起并發執行的,

(5)缺點:

A、對CPU資源占用比較多,可能因為占用一部分CPU資源導致應用程式回應變慢,
B、CMS無法處理浮動垃圾,在并發清除階段,用戶程式繼續運行,可能產生新的記憶體垃圾,這一部分垃圾出現在標記程序之后,因此,CMS無法清除,這部分垃圾稱為“浮動垃圾“
C、需要預留一部分記憶體,在垃圾回收時,給用戶程式使用,
D、基于標記-清除演算法,容易產生大量記憶體碎片,導致full GC(full GC進行記憶體碎片的整理)

6、 物件頭部分的記憶體布局:HotSpot的物件頭分為兩部分,第一部分用于存盤物件自身的運行時資料,比如哈希碼、GC分代年齡等,另外一部分用于指向方法區物件型別資料的指標,

7、 偏向鎖:偏向鎖偏向于第一個獲取它的執行緒,如果在接下來的執行程序,沒有其他執行緒獲取該鎖,則持有偏向鎖的執行緒永遠不需要同步,(當一個執行緒獲取偏向鎖,它每次進入這個鎖相關的同步塊,虛擬機不在進行任何同步操作,當有另外一個執行緒嘗試獲取這個鎖時,偏向模式宣告結束)

JVM優化:

1、一般來說,當survivor區不夠大或者占用量達到50%,就會把一些物件放到老年區,通過設定合理的eden區,survivor區及使用率,可以將年輕物件保存在年輕代,從而避免full GC,使用-Xmn設定年輕代的大小

2、對于占用記憶體比較多的大物件,一般會選擇在老年代分配記憶體,如果在年輕代給大物件分配記憶體,年輕代記憶體不夠了,就要在eden區移動大量物件到老年代,然后這些移動的物件可能很快消亡,因此導致full GC,通過設定引數:-XX:PetenureSizeThreshold=1000000,單位為B,標明物件大小超過1M時,在老年代(tenured)分配記憶體空間,

3、一般情況下,年輕物件放在eden區,當第一次GC后,如果物件還存活,放到survivor區,此后,每GC一次,年齡增加1,當物件的年齡達到閾值,就被放到tenured老年區,這個閾值可以同構-XX:MaxTenuringThreshold設定,如果想讓物件留在年輕代,可以設定比較大的閾值,

4、設定最小堆和最大堆:-Xmx-Xms穩定的堆大小堆垃圾回收是有利的,獲得一個穩定的堆大小的方法是設定-Xms和-Xmx的值一樣,即最大堆和最小堆一樣,如果這樣子設定,系統在運行時堆大小理論上是恒定的,穩定的堆空間可以減少GC次數,因此,很多服務端都會將這兩個引數設定為一樣的數值,穩定的堆大小雖然減少GC次數,但是增加每次GC的時間,因為每次GC要把堆的大小維持在一個區間內,

5、一個不穩定的堆并非毫無用處,在系統不需要使用大記憶體的時候,壓縮堆空間,使得GC每次應對一個較小的堆空間,加快單次GC次數,基于這種考慮,JVM提供兩個引數,用于壓縮和擴展堆空間,

(1)-XX:MinHeapFreeRatio 引數用于設定堆空間的最小空閑比率,默認值是40,當堆空間的空閑記憶體比率小于40,JVM便會擴展堆空間

(2)-XX:MaxHeapFreeRatio 引數用于設定堆空間的最大空閑比率,默認值是70, 當堆空間的空閑記憶體比率大于70,JVM便會壓縮堆空間,

(3)當-Xmx和-Xmx相等時,上面兩個引數無效

6、通過增大吞吐量提高系統性能,可以通過設定并行垃圾回收收集器,

(1)-XX:+UseParallelGC:年輕代使用并行垃圾回收收集器,這是一個關注吞吐量的收集器,可以盡可能的減少垃圾回收時間,

(2)-XX:+UseParallelOldGC:設定老年代使用并行垃圾回收收集器,

7、嘗試使用大的記憶體分頁:使用大的記憶體分頁增加CPU的記憶體尋址能力,從而系統的性能,-XX:+LargePageSizeInBytes 設定記憶體頁的大小

8、使用非占用的垃圾收集器,-XX:+UseConcMarkSweepGC老年代使用CMS收集器降低停頓,

9、-XXSurvivorRatio=3,表示年輕代中的分配比率:survivor:eden = 2:3

10、JVM性能調優的工具:

(1)jps(Java Process Status):輸出JVM中運行的行程狀態資訊(現在一般使用jconsole)

(2)jstack:查看java行程內執行緒的堆疊資訊,

(3)jmap:用于生成堆轉存快照

(4)jhat:用于分析jmap生成的堆轉存快照(一般不推薦使用,而是使用Ecplise Memory Analyzer)

(3)jstat是JVM統計監測工具,可以用來顯示垃圾回收資訊、類加載資訊、新生代統計資訊等,

(4)VisualVM:故障處理工具

類加載機制:

一、 概念:類加載器把class檔案中的二進制資料讀入到記憶體中,存放在方法區,然后在堆區創建一個java.lang.Class物件,用來封裝類在方法區內的資料結構,類加載的步驟如下:

1、加載:查找并加載類的二進制資料(把class檔案里面的資訊加載到記憶體里面)

2、連接:把記憶體中類的二進制資料合并到虛擬機的運行時環境中

(1)驗證:確保被加載的類的正確性,包括:

A、類檔案的結構檢查:檢查是否滿足Java類檔案的固定格式
B、語意檢查:確保類本身符合Java的語法規范
C、位元組碼驗證:確保位元組碼流可以被Java虛擬機安全的執行,位元組碼流是操作碼組成的序列,每一個操作碼后面都會跟著一個或者多個運算元,位元組碼檢查這個步驟會檢查每一個操作碼是否合法,
D、二進制兼容性驗證:確保相互參考的類之間是協調一致的,

(2)準備:為類的靜態變數分配記憶體,并將其初始化為默認值

(3)決議:把類中的符號參考轉化為直接參考(比如說方法的符號參考,是有方法名和相關描述符組成,在決議階段,JVM把符號參考替換成一個指標,這個指標就是直接參考,它指向該類的該方法在方法區中的記憶體位置)

3、初始化:為類的靜態變數賦予正確的初始值,當靜態變數的等號右邊的值是一個常量運算式時,不會呼叫static代碼塊進行初始化,只有等號右邊的值是一個運行時運算出來的值,才會呼叫static初始化,

二、雙親委派模型:

1、當一個類加載器收到類加載請求的時候,它首先不會自己去加載這個類的資訊,而是把該請求轉發給父類加載器,依次向上,所以所有的類加載請求都會被傳遞到父類加載器中,只有當父類加載器中無法加載到所需的類,子類加載器才會自己嘗試去加載該類,當當前類加載器和所有父類加載器都無法加載該類時,拋出ClassNotFindException例外,

2、意義:提高系統的安全性,用戶自定義的類加載器不可能加載應該由父加載器加載的可靠類,(比如用戶定義了一個惡意代碼,自定義的類加載器首先讓系統加載器去加載,系統加載器檢查該代碼不符合規范,于是就不繼續加載了)

3、定義類加載器:如果某個類加載器能夠加載一個類,那么這個類加載器就叫做定義類加載器4、初始類加載器:定義類加載器及其所有子加載器都稱作初始類加載器,5、運行時包:

(1)由同一個類加載器加載并且擁有相同包名的類組成運行時包

(2)只有屬于同一個運行時包的類,才能訪問包可見(default)的類和類成員,作用是 限制用戶自定義的類冒充核心類別庫的類去訪問核心類別庫的包可見成員,

6、加載兩份相同的class物件的情況:A和B不屬于父子類加載器關系,并且各自都加載了同一個類,

三、特點:

1、全盤負責:當一個類加載器加載一個類時,該類所依賴的其他類也會被這個類加載器加載到記憶體中,

2、快取機制:所有的Class物件都會被快取,當程式需要使用某個Class時,類加載器先從快取中查找,找不到,才從class檔案中讀取資料,轉化成Class物件,存入快取中,

三、 類加載器:兩種型別的類加載器:

1、 JVM自帶的類加載器(3種):

(1)根類加載器(Bootstrap):a、C++撰寫的,程式員無法在程式中獲取該類b、負責加載虛擬機的核心庫,比如java.lang.Objectc、沒有繼承ClassLoader類

(2)擴展類加載器(Extension):a、Java撰寫的,從指定目錄中加載類別庫b、父加載器是根類加載器c、是ClassLoader的子類d、如果用戶把創建的jar檔案放到指定目錄中,也會被擴展加載器加載,

(3)系統加載器(System)或者應用加載器(App):a、Java撰寫的b、父加載器是擴展類加載器c、從環境變數或者class.path中加載類d、是用戶自定義類加載的默認父加載器e、是ClassLoader的子類

2、用戶自定義的類加載器:

(1)Java.lang.ClassLoader類的子類

(2)用戶可以定制類的加載方式

(3)父類加載器是系統加載器

(4)撰寫步驟:A、繼承ClassLoaderB、重寫findClass方法,從特定位置加載class檔案,得到位元組陣列,然后利用defineClass把位元組陣列轉化為Class物件

(5)為什么要自定義類加載器?A、可以從指定位置加載class檔案,比如說從資料庫、云端加載class檔案B、加密:Java代碼可以被輕易的反編譯,因此,如果需要對代碼進行加密,那么加密以后的代碼,就不能使用Java自帶的ClassLoader來加載這個類了,需要自定義ClassLoader,對這個類進行解密,然后加載,

問題:Java程式對類的執行有幾種方式:

1、 主動使用(6種情況):JVM必須在每個類“首次 主動使用”的時候,才會初始化這些類,

(1) 創建類的實體

(2) 讀寫某個類或者介面的靜態變數

(3) 呼叫類的靜態方法

(4) 同過反射的API(Class.forName())獲取類

(5) 初始化一個類的子類

(6) JVM啟動的時候,被標明啟動類的類(包含Main方法的類)只有當程式使用的靜態變數或者靜態方法確實在該類中定義時,該可以認為是對該類或者介面的主動使用,

2、 被動使用:除了主動使用的6種情況,其他情況都是被動使用,都不會導致類的初始化,

3、 JVM規范允許類加載器在預料某個類將要被使用的時候,就預先加載它,如果該class檔案缺失或者存在錯誤,則在程式“首次 主動使用”的時候,才報告這個錯誤,(Linkage Error錯誤),如果這個類一直沒有被程式“主動使用”,就不會報錯,

類加載機制與介面:

1、 當Java虛擬機初始化一個類時,不會初始化該類實作的介面,

2、 在初始化一個介面時,不會初始化這個介面父介面,

3、 只有當程式首次使用該介面的靜態變數時,才導致該介面的初始化,

ClassLoader:

1、 呼叫Classloader的loadClass方法去加載一個類,不是主動使用,因此不會進行類的初始化,

類的卸載:

1、 有JVM自帶的三種類加載器(根、擴展、系統)加載的類始終不會卸載,因為JVM始終參考這些類加載器,這些類加載器使用參考他們所加載的類,因此這些Class類物件始終是可到達的,2、 由用戶自定義類加載器加載的類,是可以被卸載的,

補充:

  • JDK和JRK

(1)JDK :Java Development Kit,開發的時候用到的類包,

(2)JRE :Java Runtime Environment,Java運行的基礎,包含運行時需要的所有類別庫,

  • 圖解java檔案轉化成機器碼
  •  

     JVM虛擬機先將java檔案編譯成class檔案(位元組碼檔案),然后再將class檔案轉換成所有作業系統都能運行的機器指令,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/157905.html

標籤:Java

上一篇:新來的"大神"用策略模式把if else給"優化"了,技術總監說:能不能想好了再改?

下一篇:常用的 Git 命令,給你準備好了!

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more