偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Java中什么是類加載?類加載的過程?

開發(fā) 前端
二進(jìn)制流可以來源于 class 文件,或通過字節(jié)碼工具生成的字節(jié)碼或來自于網(wǎng)絡(luò)。只要符合格式的二進(jìn)制流,JVM 來者不拒。

類加載指的是把類加載到 JVM 中。把二進(jìn)制流存儲(chǔ)到內(nèi)存中,之后經(jīng)過一番解析、處理轉(zhuǎn)化成可用的 class 類。

二進(jìn)制流可以來源于 class 文件,或通過字節(jié)碼工具生成的字節(jié)碼或來自于網(wǎng)絡(luò)。只要符合格式的二進(jìn)制流,JVM 來者不拒。

虛擬機(jī)遇到?條 new 指令時(shí),?先將去檢查這個(gè)指令的參數(shù)是否能在常量池中定位到這個(gè)類的符號(hào)引?,并且檢查這個(gè)符號(hào)引?代表的類是否已被加載過、解析和初始化過。如果沒有,那必須先執(zhí)?相應(yīng)的類加載過程。類加載過程包括了加載、連接、初始化三個(gè)階段,其中連接還可以分為驗(yàn)證、準(zhǔn)備、解析。

圖片圖片

加載

將二進(jìn)制流讀入內(nèi)存中,生成一個(gè) Class 對(duì)象。

在加載階段,虛擬機(jī)需要完成以下三件事情:

  • 通過一個(gè)類的全限定名來獲取其定義的二進(jìn)制字節(jié)流。
  • 將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
  • 在Java堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為對(duì)方法區(qū)中這些數(shù)據(jù)的訪問入口。

圖片圖片

這個(gè)階段既可以使用系統(tǒng)提供的類加載器來完成加載,也可以自定義自己的類加載器來完成加載。

驗(yàn)證

確保Class文件的字節(jié)流中包含的信息符合JVM規(guī)范,保證在運(yùn)行后不會(huì)危害虛擬機(jī)自身的安全。即安全性檢查,主要包括四種驗(yàn)證:

  • 文件格式驗(yàn)證: 驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范;例如: 是否以0xCAFEBABE開頭、主次版本號(hào)是否在當(dāng)前虛擬機(jī)的處理范圍之內(nèi)、常量池中的常量是否有不被支持的類型。
  • 元數(shù)據(jù)驗(yàn)證:: 對(duì)字節(jié)碼描述的信息進(jìn)行語義分析(注意: 對(duì)比javac編譯階段的語義分析),以保證其描述的信息符合Java語言規(guī)范的要求;例如: 這個(gè)類是否有父類,除了java.lang.Object之外。
  • 字節(jié)碼驗(yàn)證:通過數(shù)據(jù)流和控制流分析,確定程序語義是合法的、符合邏輯的。
  • 符號(hào)引用驗(yàn)證:確保解析動(dòng)作能正確執(zhí)行

驗(yàn)證階段是非常重要的,但不是必須的,它對(duì)程序運(yùn)行期沒有影響,如果所引用的類經(jīng)過反復(fù)驗(yàn)證,那么可以考慮采用-Xverifynone參數(shù)來關(guān)閉大部分的類驗(yàn)證措施,以縮短虛擬機(jī)類加載的時(shí)間。

準(zhǔn)備

準(zhǔn)備階段是正式為static 變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些內(nèi)存都將在方法區(qū)中分配。

static變量在分配空間和賦值是在兩個(gè)階段完成的。分配空間在準(zhǔn)備階段完成,賦值在初始化階段完成。也就是說這里給類變量設(shè)置初始值,設(shè)置的是數(shù)據(jù)類型默認(rèn)的零值(如0、0L、null、false等)。

  • 如果 static 變量是 ?nal 的基本類型,以及字符串常量,那么編譯階段值就確定了,賦值在準(zhǔn)備階段完成。
  • 如果 static 變量是 ?nal 的,但屬于引用類型,那么賦值會(huì)在初始化階段完成。

解析

將常量池內(nèi)的符號(hào)引用替換為直接引用的過程。符號(hào)引用用于描述目標(biāo),直接引用直接指向目標(biāo)的地址。

  • 未解析時(shí),常量池中的看到的對(duì)象僅是符號(hào),未真正的存在于內(nèi)存中。
  • 解析以后,會(huì)將常量池中的符號(hào)引用解析為直接引用。

初始化

初始化階段會(huì)執(zhí)行cinit方法來為 類變量static變量 賦上定義的值并執(zhí)行類中的靜態(tài)代碼塊;這里的賦值才是代碼里面的賦值,準(zhǔn)備階段只是設(shè)置初始值占個(gè)坑。

在Java中對(duì)類變量進(jìn)行初始值設(shè)定有兩種方式:

  • 聲明類變量是指定初始值
  • 使用靜態(tài)代碼塊為類變量指定初始值

何時(shí)進(jìn)行類加載?

  • 定義了main的類,啟動(dòng)main方法時(shí)該類會(huì)被加載
  • 創(chuàng)建類的實(shí)例,即new對(duì)象的時(shí)候
  • 訪問類的靜態(tài)方法
  • 訪問類的靜態(tài)變量
  • 反射 Class.forName()

JVM初始化步驟?

  • 假如這個(gè)類還沒有被加載和連接,則程序先加載并連接該類
  • 假如該類的直接父類還沒有被初始化,則先初始化其直接父類
  • 假如類中有初始化語句,則系統(tǒng)依次執(zhí)行這些初始化語句

初始化發(fā)生的時(shí)機(jī)?

概括得說,類初始化是【懶惰的】,只有當(dāng)對(duì)類的主動(dòng)使用的時(shí)候才會(huì)導(dǎo)致類的初始化。

  • main 方法所在的類,總會(huì)被首先初始化
  • 首次訪問這個(gè)類的靜態(tài)變量或靜態(tài)方法時(shí)
  • 子類初始化,如果父類還沒初始化,會(huì)引發(fā)父類初始化
  • 子類訪問父類的靜態(tài)變量,只會(huì)觸發(fā)父類的初始化
  • Class.forName new 會(huì)導(dǎo)致初始化

不會(huì)導(dǎo)致類初始化的情況?

  • 訪問類的 static final 靜態(tài)常量(基本類型和字符串)不會(huì)觸發(fā)初始化。
  • 類對(duì)象.class 不會(huì)觸發(fā)初始化
  • 創(chuàng)建該類的數(shù)組不會(huì)觸發(fā)初始化
  • 類加載器的 loadClass 方法
  • Class.forName 的參數(shù) 2 為 false 時(shí)

cinit方法如果執(zhí)行失敗了怎么辦,這個(gè)類還能用嗎?

  • 在Java類加載的過程中,cinit 方法實(shí)際上指的是類的靜態(tài)初始化方法,也就是類的靜態(tài)代碼塊或者靜態(tài)變量的初始化代碼。如果類的靜態(tài)初始化方法執(zhí)行失敗,通常會(huì)導(dǎo)致類的初始化失敗,這意味著這個(gè)類不能被正常使用。會(huì)拋出異常,如 ExceptionInInitializerError
  • 在Java中,類的靜態(tài)初始化方法只會(huì)執(zhí)行一次,無論類被加載多少次,靜態(tài)初始化方法只會(huì)在首次加載類的時(shí)候執(zhí)行。因此,cinit 方法不會(huì)多次執(zhí)行。一旦類的靜態(tài)初始化方法執(zhí)行過,后續(xù)對(duì)同一個(gè)類的加載都不會(huì)再次觸發(fā)靜態(tài)初始化方法的執(zhí)行。這種機(jī)制確保了類的靜態(tài)初始化只會(huì)在需要的時(shí)候執(zhí)行一次,避免了不必要的開銷和重復(fù)操作。

分配內(nèi)存

在類加載后,接下來虛擬機(jī)將為新?對(duì)象分配內(nèi)存。

分配在哪?

主要就是根據(jù)JVM的分配機(jī)制:對(duì)象優(yōu)先分配Eden

  1. 先TLAB分配
  2. 再通過CAS在Eden區(qū)分配
  3. 大對(duì)象直接分配到老年代

TLAB:線程本地分配緩沖區(qū),為每?個(gè)線程預(yù)先在 Eden 區(qū)分配?塊?私有的緩存區(qū)域,JVM 在給線程中的對(duì)象分配內(nèi)存時(shí),?先在 TLAB 分配,當(dāng)對(duì)象?于 TLAB 中的剩余內(nèi)存或 TLAB 的內(nèi)存已?盡時(shí)(或者未開啟TLAB),再采?上述的 CAS 進(jìn)?內(nèi)存分配。默認(rèn)情況TLAB僅占每個(gè)Eden區(qū)域的1%。它的主要目的是在多線程并發(fā)環(huán)境下需要進(jìn)行內(nèi)存分配的時(shí)候,減少線程之間對(duì)于內(nèi)存分配區(qū)域的競(jìng)爭(zhēng),加速內(nèi)存分配的速度。

為什么要CAS分配內(nèi)存?

多個(gè)并發(fā)執(zhí)行的線程需要?jiǎng)?chuàng)建對(duì)象、申請(qǐng)分配內(nèi)存的時(shí)候,有可能在 Java 堆的同一個(gè)位置申請(qǐng),這時(shí)就需要對(duì)擬分配的內(nèi)存區(qū)域進(jìn)行加鎖或者采用 CAS 等操作,保證這個(gè)區(qū)域只能分配給一個(gè)線程。

JVM對(duì)象分配內(nèi)存如何保證線程安全

在JVM中,為對(duì)象分配內(nèi)存的過程需要確保線程安全,因?yàn)樵诙嗑€程環(huán)境下,多個(gè)線程可能會(huì)同時(shí)嘗試創(chuàng)建對(duì)象。為了保證內(nèi)存分配的線程安全性,JVM采用了以下幾種機(jī)制和技術(shù):

  1. TLAB(Thread Local Allocation Buffer):

當(dāng)一個(gè)線程需要分配對(duì)象時(shí),首先會(huì)嘗試在TLAB中進(jìn)行分配。如果TLAB有足夠的空間,分配過程就是線程安全的,因?yàn)闆]有其他線程訪問這個(gè)內(nèi)存塊。

不足:當(dāng)TLAB空間不足時(shí),線程需要請(qǐng)求一個(gè)新的TLAB或者直接從共享堆中分配,這個(gè)過程需要一定的同步機(jī)制。

  1. CAS(Compare-And-Swap)機(jī)制:  當(dāng)TLAB耗盡或在涉及到跨線程的堆內(nèi)存分配時(shí),CAS有效避免了競(jìng)爭(zhēng)條件。
  2. 分代收集:  雖然不是直接用于線程安全,但分代收集(年輕代、老年代、永久代/元空間)使得內(nèi)存管理更高效,減少了直接競(jìng)爭(zhēng)的機(jī)會(huì)。

結(jié)合:TLAB一般對(duì)年輕代的內(nèi)存分配進(jìn)行優(yōu)化,更加局部化的內(nèi)存管理有助于線程安全。
通過運(yùn)用這些機(jī)制,JVM能夠在多線程環(huán)境下高效而安全地進(jìn)行內(nèi)存分配,并最大限度地減少同步操作帶來的性能損耗。這樣設(shè)計(jì)不僅提升了性能,也保證了對(duì)象內(nèi)存分配的安全性和一致性。

說說對(duì)象分配規(guī)則

在Java中,對(duì)象分配規(guī)則是關(guān)于如何為新對(duì)象分配內(nèi)存的一套規(guī)則,以確保內(nèi)存的有效使用和對(duì)象的正確初始化。以下是關(guān)于對(duì)象分配的主要規(guī)則:

  1. 內(nèi)存分配:新對(duì)象通常在堆內(nèi)存中分配內(nèi)存空間。
  2. 對(duì)象頭:在為對(duì)象分配內(nèi)存空間后,Java虛擬機(jī)會(huì)為對(duì)象分配一個(gè)對(duì)象頭。對(duì)象頭包含了一些關(guān)于對(duì)象的元信息,如對(duì)象的哈希碼、鎖狀態(tài)、垃圾回收信息等。
  3. 零值初始化:在對(duì)象內(nèi)存分配后,所有的成員變量會(huì)被初始化為零值。具體的零值取決于變量的數(shù)據(jù)類型。例如,整數(shù)類型會(huì)初始化為0,布爾類型會(huì)初始化為false,對(duì)象引用會(huì)初始化為null。
  4. 構(gòu)造函數(shù)調(diào)用:一旦對(duì)象內(nèi)存分配和零值初始化完成,Java虛擬機(jī)會(huì)調(diào)用對(duì)象的構(gòu)造函數(shù)。
  5. 對(duì)象引用:最后,new 關(guān)鍵字會(huì)返回對(duì)象的引用,將這個(gè)引用分配給一個(gè)變量,以便后續(xù)可以通過該變量訪問對(duì)象的屬性和方法。
  6. 垃圾回收管理:Java虛擬機(jī)會(huì)自動(dòng)管理對(duì)象的內(nèi)存。如果對(duì)象不再被引用,它會(huì)被標(biāo)記為垃圾,并在適當(dāng)?shù)臅r(shí)機(jī)由垃圾回收器回收,釋放占用的內(nèi)存。

圖片圖片

這些規(guī)則確保了對(duì)象在創(chuàng)建時(shí)的正確初始化和內(nèi)存管理。對(duì)于程序員來說,最重要的是編寫好構(gòu)造函數(shù)以確保對(duì)象在創(chuàng)建后具有合適的初始狀態(tài),并且不忘記在不再需要對(duì)象時(shí)將引用置為null,以便垃圾回收器能夠回收不再使用的對(duì)象。

何時(shí)進(jìn)行類卸載?

類的卸載條件很多,需要滿足以下三個(gè)條件,并且滿足了也不一定會(huì)被卸載:

  • 該類所有的實(shí)例都已經(jīng)被回收,也就是堆中不存在該類的任何實(shí)例。
  • 加載該類的 ClassLoader 已經(jīng)被回收。
  • 該類對(duì)應(yīng)的 Class 對(duì)象沒有在任何地方被引用,也就無法在任何地方通過反射訪問該類方法。

可以通過 -Xnoclassgc 參數(shù)來控制是否對(duì)類進(jìn)行卸載。

Java虛擬機(jī)將結(jié)束生命周期的幾種情況?(什么情況會(huì)導(dǎo)致JVM退出)

  • 正常程序終止:  當(dāng)程序執(zhí)行完main方法,包括所有非守護(hù)線程都終止時(shí),JVM將正常退出。
  • 調(diào)用System.exit(int status):  顯式調(diào)用System.exit()方法,以指定的狀態(tài)碼終止當(dāng)前運(yùn)行的Java虛擬機(jī)。
  • 未捕獲的異?;蝈e(cuò)誤:  如果某個(gè)線程拋出的異常沒有被捕獲,并且此異常傳播到了主線程,JVM可能會(huì)終止。
  • Runtime.halt(int)或崩潰:

直接調(diào)用Runtime.halt()會(huì)立即停止Java進(jìn)程,類似于突然終止程序而不調(diào)用任何鉤子。

JVM的致命錯(cuò)誤(如內(nèi)存訪問違規(guī))也可能導(dǎo)致崩潰并退出。

  • 外部命令強(qiáng)制關(guān)閉:  例如通過操作系統(tǒng)的任務(wù)管理器或者控制臺(tái)命令,如kill命令?;蛘卟僮飨到y(tǒng)出現(xiàn)錯(cuò)誤而導(dǎo)致Java虛擬機(jī)進(jìn)程終止
責(zé)任編輯:武曉燕 來源: SevenCoding
相關(guān)推薦

2019-12-09 15:08:30

JavaTomcatWeb

2025-07-01 07:41:37

Java類加載器雙親委派

2012-11-06 10:19:18

Java自定義加載Java類

2024-06-24 14:52:50

Android類加載器

2023-12-06 12:11:43

類加載器雙親委派模型

2021-07-05 06:51:43

Java機(jī)制類加載器

2012-02-14 13:39:57

Java

2012-02-09 10:31:17

Java

2019-07-24 08:34:35

Java對(duì)象數(shù)據(jù)結(jié)構(gòu)

2024-03-08 08:26:25

類的加載Class文件Java

2021-05-08 09:02:19

Java加載器

2009-02-03 09:42:53

JAVA類JVM指令forName方法

2024-03-12 07:44:53

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

2024-12-02 09:01:23

Java虛擬機(jī)內(nèi)存

2023-01-28 10:40:56

Java虛擬機(jī)代碼

2021-01-06 09:01:05

javaclass

2020-12-30 08:01:07

Java隔離加載

2021-01-29 06:06:12

JDK15類加載Java

2024-09-06 09:37:45

WebApp類加載器Web 應(yīng)用

2017-03-08 10:30:43

JVMJava加載機(jī)制
點(diǎn)贊
收藏

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