精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
簡述JVM(1)——類加載器和運(yùn)行時(shí)數(shù)據(jù)區(qū)

大家好,我是KookNut39也是Tommy,在CSDN寫文,寫Java時(shí)候我是Tommy,分享一些自己認(rèn)為在學(xué)習(xí)過程中比較重要的東西,致力于幫助初學(xué)者入門,希望可以幫助你進(jìn)步。感興趣的歡迎關(guān)注博主,和博主一起學(xué)習(xí)Java知識。大家還可以去專欄查看之前的文章,希望未來能和大家共同探討技術(shù)。

文章目錄

了解JVM(Java虛擬機(jī))首先我們必須了解VM(虛擬機(jī))是什么。

所謂虛擬機(jī)(Virtual Machine),就是一臺虛擬的計(jì)算機(jī)。它是一款軟件,用來執(zhí)行一系列虛擬計(jì)算機(jī)指令。大體上,虛擬機(jī)可以分為系統(tǒng)虛擬機(jī)和程序虛擬機(jī)。

我們經(jīng)常見到的 VMware 就屬于系統(tǒng)虛擬機(jī),它是完全對物理計(jì)算機(jī)的仿真,提供了一個(gè)可運(yùn)行完整操作系統(tǒng)的軟件平臺。

程序虛擬機(jī)典型的代表就是 Java 虛擬機(jī)了,它專門為執(zhí)行某個(gè)單個(gè)計(jì)算機(jī)程序而設(shè)計(jì)。在 Java 虛擬機(jī)中執(zhí)行的指令我們稱為 Java 字節(jié)碼指令。無論是系統(tǒng)虛擬機(jī)還是程序虛擬機(jī),在上面運(yùn)行的軟件都被限制于虛擬機(jī)提供的資源。Java 虛擬機(jī)是一種執(zhí)行 Java 字節(jié)碼文件的虛擬計(jì)算機(jī),它擁有獨(dú)立的運(yùn)行機(jī)制。

Java 技術(shù)的核心就是 Java 虛擬機(jī),因?yàn)樗械?Java 程序都運(yùn)行在 Java 虛擬機(jī)內(nèi)部。

JVM概述

JVM就是Java虛擬機(jī),虛擬機(jī)就是一臺虛擬的計(jì)算機(jī),是一款軟件。Java 虛擬機(jī)就是二進(jìn)制字節(jié)碼的運(yùn)行環(huán)境,負(fù)責(zé)裝載字節(jié)碼到其內(nèi)部,解釋或編譯為對應(yīng)平臺上的機(jī)器碼指令執(zhí)行,每一條 Java 指令,Java 虛擬機(jī)中都有詳細(xì)定義,如怎么取操作數(shù),怎么處理操作數(shù),處理結(jié)果放在哪兒等。JVM是運(yùn)行在操作系統(tǒng)上的,不與硬件直接交互。

JVM整體的四個(gè)部分

JVM整體組成可以分為4個(gè)部分:

  1. 類加載器(ClassLoader)
  2. 運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area)
  3. 執(zhí)行引擎(Execution Engine)
  4. 本地方法接口(Native Interface)

程序在執(zhí)行之前先要把 Java 代碼轉(zhuǎn)換成字節(jié)碼(.class 文件),JVM 首先需要把字節(jié)碼通過一定的類加載器(Class Loader)把文件加載到內(nèi)存中運(yùn)行時(shí)數(shù)據(jù)區(qū)(Runtime Data Area),而字節(jié)碼文件是 JVM 的一套指令集規(guī)范,并不能直接交給底層操作系統(tǒng)去執(zhí)行,因此需要特定的命令解析器(執(zhí)行引擎(Execution Engine)) 將字節(jié)碼翻譯成底層系統(tǒng)指令再交由 CPU 去執(zhí)行,而這個(gè)過程中需要調(diào)用其他語言的接口(本地方法接口(Native Interface)) 來實(shí)現(xiàn)。整個(gè)程序的功能,這就是這 4 個(gè)主要組成部分的職責(zé)與功能。

1.1 類加載器

Java編譯器會為虛擬機(jī)轉(zhuǎn)換源指令。虛擬機(jī)代碼存儲在以 .class 為擴(kuò)展名的類文件中,每個(gè)類文件都包含某個(gè)類或者接口的定義和實(shí)現(xiàn)代碼。

? ——Java核心基礎(chǔ)(卷二)

1.1.1 類加載器過程

類加載器子系統(tǒng)負(fù)責(zé)從文件系統(tǒng)或者網(wǎng)絡(luò)中加載 class 文件, class 文件在文件開頭有特定的文件標(biāo)識(字節(jié)碼文件都以 CA FE BA BE 標(biāo)識開頭)。classLoader 只負(fù)責(zé) class 文件的加載,至于它是否可以運(yùn)行,則由執(zhí)行引擎決定。加載的類信息存放于一塊稱為方法區(qū)的內(nèi)存空間。除了類的信息外,方法區(qū)中還會存放運(yùn)行時(shí)常量池信息,可能還包括字符串字面量和數(shù)字常量(這部分常量信息是 class 文件中常量池部分的內(nèi)存映射)。

類加載的過程

1.加載過程是把class文件(字節(jié)碼文件)加載到內(nèi)存中(I/O讀寫)。類加載器把文件加載到內(nèi)存中,會為每個(gè)類創(chuàng)建一個(gè)Class類的對象,調(diào)用Class類中的方法獲取類的相關(guān)信息。

2.驗(yàn)證是檢驗(yàn)加載的類是否有正確的內(nèi)部結(jié)構(gòu)并和其他類協(xié)調(diào)一致。

3.準(zhǔn)備階段為類的靜態(tài)屬性分配內(nèi)存,并設(shè)置默認(rèn)初始值(不包含final修飾的static常量),也不會給實(shí)例變量初始化。

4.解析是將二進(jìn)制數(shù)據(jù)中的符號引用替換成直接引用(符號引用是用一組符號描述所引用的目標(biāo);直接引用是指向目標(biāo)的指針)。

5.類初始化

? 5.1 什么時(shí)候初始化類

? 1 )創(chuàng)建類的實(shí)例,也就是 new 一個(gè)對象
? 2)訪問某個(gè)類或接口的靜態(tài)變量,或者對該靜態(tài)變量賦值
? 3)調(diào)用類的靜態(tài)方法
? 4)反射
? 5)初始化一個(gè)類的子類(會首先初始化子類的父類)

? 5.2 類的初始化順序:

? 父類static --> 子類static --> 父類構(gòu)造方法 --> 子類構(gòu)造方法

1.1.2類加載器的分類

1.1.2.1啟動類加載器(引導(dǎo)類加載器)

是由c/c++實(shí)現(xiàn),用來加載Java的核心類庫

1.1.2.2擴(kuò)展類加載器

由Java實(shí)現(xiàn),派生于 ClassLoader 類。上層類加載器是引導(dǎo)類加載器(啟動類加載器),加載底層類庫

1.1.2.3應(yīng)用程序類加載器

Java實(shí)現(xiàn),派生于 ClassLoader 類。上層類加載器是擴(kuò)展類加載器,加載自定義類。

1.1.3 雙親委派機(jī)制

加載類時(shí),向上委派,交給最上層的加載器啟動類加載器加載核心類庫,加載不到就用擴(kuò)展類加載器加載底層類庫,加載不到就用應(yīng)用程序類加載器加載自定義類

雙親委派機(jī)制的優(yōu)點(diǎn)

  1. 安全,可避免用戶自己編寫的類動態(tài)替換 Java 的核心類(自定義的類名和Java核心類名相同)
  2. 避免全限定命名的類重復(fù)加載(使用了 findLoadClass()判斷當(dāng)前類是否已加載)

1.1.4沙箱安全機(jī)制

作用是防止惡意代碼污染 Java 源代碼

如果一個(gè)類在引導(dǎo)類加載器那里就加載到了,先找到先使用,所以就使用引導(dǎo)類加載器里面的類,后面的一概不能使用,這就保證了不被惡意代碼污染。

1.1.5 類的主動使用和被動使用

JVM 規(guī)定,每個(gè)類或者接口被首次主動使用時(shí)才對其進(jìn)行初始化,有主動使用,自然就有被動使用。
主動使用:

  1. 通過new關(guān)鍵字被導(dǎo)致類的初始化,這是大家經(jīng)常使用的初始化一個(gè)類的方式,
    他肯定會導(dǎo)致類的加載并且初始化
  2. 訪問類的靜態(tài)變量,包括讀取和更新
  3. 訪問類的靜態(tài)方法
  4. 對某個(gè)類進(jìn)行反射操作,會導(dǎo)致類的初始化
  5. 初始化子類會導(dǎo)致父類的的初始化
  6. 執(zhí)行該類的 main 函數(shù)

被動使用:其實(shí)除了上面的幾種主動使用其余就是被動使用了

注意:主動使用和被動使用的區(qū)別在于類是否會被初始化.

  1. 引用該類的靜態(tài)常量,注意是常量,不會導(dǎo)致初始化,但是也有意外,這里的常量是指已經(jīng)指定字面量的常量,對于那些需要一些計(jì)算才能得出結(jié)果的常量就會導(dǎo)致初始化,比如:
    public final static int number = 5 ; //不會導(dǎo)致類初始化,被動使用
    public final static int random = new Random().nextInt() ; //會導(dǎo)致類的初始化,主動使用
  2. 構(gòu)造某個(gè)類的數(shù)組時(shí)不會導(dǎo)致該類的初始化,比如:
    Student[] students = new Student[10] ;

1.2 運(yùn)行時(shí)數(shù)據(jù)區(qū)

JVM 的運(yùn)行時(shí)數(shù)據(jù)區(qū),不同虛擬機(jī)實(shí)現(xiàn)可能略微有所不同,但都會遵從 Java 虛擬機(jī)規(guī)范,Java 8 虛擬機(jī)規(guī)范規(guī)定,Java 虛擬機(jī)所管理的內(nèi)存將會包括程序計(jì)數(shù)器、Java虛擬機(jī)棧、本地方法棧、Java堆、方法區(qū)。

1.2.1 程序計(jì)數(shù)器(Program Counter Register)

JVM 中的程序計(jì)數(shù)寄存器中的 Register 命名源于CPU 的寄存器,寄存器存儲指令相關(guān)的現(xiàn)場信息。這里,并非是廣義上所指的物理寄存器,或許將其翻譯為 PC 計(jì)數(shù)器(或指令計(jì)數(shù)器)會更加貼切(也稱為程序鉤子)。JVM 中的PC 寄存器是對物理 PC 寄存器的一種抽象模擬。

  1. 它是一塊很小的內(nèi)存空間,幾乎可以忽略不計(jì),但是它是運(yùn)行速度最快的存儲區(qū)域。
  2. 在 JVM 規(guī)范中,每個(gè)線程都有它自己的程序計(jì)數(shù)器,是線程私有的,生命周期與線程生命周期保持一致。
  3. 任何時(shí)間一個(gè)線程都只有一個(gè)方法在執(zhí)行,也就是所謂的當(dāng)前方法,程序計(jì)數(shù)器會存儲當(dāng)前線程正在執(zhí)行的Java方法的JVM指令地址。如果是在執(zhí)行native方法,則是未指定值(undefined)。
  4. 它是程序控制流的指示器,分支、循環(huán)、跳轉(zhuǎn)、異常處理,線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成。
  5. 字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令。
  6. 它是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何 OutOfMemoryError 情況的區(qū)域.

那么為什么使用程序計(jì)數(shù)器記錄當(dāng)前線程的執(zhí)行地址呢?我們都知道CPU需要不停的切換各個(gè)線程,這時(shí)候從其他線程切換到這個(gè)線程時(shí),根據(jù)程序計(jì)數(shù)器就知道該從哪里開始執(zhí)行了。JVM 的字節(jié)碼解釋器就需要通過改變程序計(jì)數(shù)器的值來明確下一條應(yīng)該執(zhí)行什么樣的字節(jié)碼指令,由于這個(gè)原因,必須為每個(gè)線程分配一個(gè)程序計(jì)數(shù)器,這樣各個(gè)線程就可以互不干擾。

1.2.2 Java 虛擬機(jī)棧(Java Virtual Machine Stacks)

首先我們需要學(xué)會區(qū)分棧和堆:

棧是運(yùn)行時(shí)的單位,而堆時(shí)存儲的單位。

  1. 棧:解決程序的運(yùn)行問題,即程序如何執(zhí)行,或者說如何處理數(shù)據(jù)。
  2. 堆:解決的是數(shù)據(jù)存儲的問題,即數(shù)據(jù)怎么放,放在哪兒。
1.2.2.1 Java虛擬機(jī)棧概述

每個(gè)線程在創(chuàng)建時(shí)都會創(chuàng)建一個(gè)虛擬機(jī)棧,其內(nèi)部保存一個(gè)個(gè)棧幀,對應(yīng)著一次方法的調(diào)用。Java 虛擬機(jī)棧是線程私有的。生命周期和線程一致。棧中的數(shù)據(jù)都以棧幀為單位存儲。棧幀是一個(gè)內(nèi)存區(qū)塊,是一個(gè)數(shù)據(jù)集,維系著方法執(zhí)行過程中的各種數(shù)據(jù)信息。

作用:主要負(fù)責(zé)Java程序的運(yùn)行,保存方法內(nèi)的局部變量,還有部分結(jié)果,還參與方法的調(diào)用和返回。

棧是一種快速有效的分配存儲方式,訪問速度僅次于程序計(jì)數(shù)器。JVM 直接對 Java 棧的操作只有兩個(gè):

  1. 調(diào)用方法,進(jìn)棧。
  2. 執(zhí)行結(jié)束后出棧。

注意:對于棧來說不存在垃圾回收問題。

1.2.2.2棧的運(yùn)行原理
  1. JVM 直接對 Java 棧的操作只有兩個(gè),就是對棧幀的壓棧和出棧,遵循”先進(jìn)后出“后進(jìn)先出的原則。
  2. 在一條活動的線程中,一個(gè)時(shí)間點(diǎn)上,只會有一個(gè)活動棧。即只有當(dāng)前在執(zhí)行的方法的棧幀(此時(shí)這個(gè)棧楨在棧頂)是有效地,這個(gè)棧幀被稱為當(dāng)前棧(Current Frame),與當(dāng)前棧幀對應(yīng)的方法稱為當(dāng)前方法(Current Method),定義這個(gè)方法的類稱為當(dāng)前類(Current Class)。
  3. 執(zhí)行引擎運(yùn)行的所有字節(jié)碼指令只針對當(dāng)前棧幀進(jìn)行操作。
  4. 如果在該方法中調(diào)用了其他方法,對應(yīng)的新的棧幀就會被創(chuàng)建出來,放在棧的頂端,成為新的當(dāng)前棧幀。
  5. 不同線程中所包含的棧幀(方法)是不允許存在相互引用的,即不可能在一個(gè)棧中引用另一個(gè)線程的棧幀(方法)。
  6. 如果當(dāng)前方法調(diào)用了其他方法,方法返回之際,當(dāng)前棧幀會傳回此方法的執(zhí)行結(jié)果給前一個(gè)棧幀,接著虛擬機(jī)會丟棄當(dāng)前棧幀,使得前一個(gè)棧幀重新成為當(dāng)前棧幀。Java 方法有兩種返回的方式,一種是正常的函數(shù)返回,使用 return 指令,另一種是拋出異常。不管哪種方式,都會導(dǎo)致棧幀被彈出.

1.2.2.3 棧幀的內(nèi)部

每個(gè)棧幀中都有:局部變量表、操作數(shù)棧、動態(tài)鏈接、方法返回地址、一下附加信息。

1.2.2.3.1局部變量表(Local Variables)

局部變量表用于存放方法參數(shù)和方法內(nèi)部定義的局部變量。

對于基本數(shù)據(jù)類型的變量,則直接存儲它的值,對于引用類型的變量,則存的是指向?qū)ο蟮囊谩?/p>

1.2.2.3.2 操作數(shù)棧(Operand Stack)(或表達(dá)式棧)

程序中的所有計(jì)算過程都是在借助于操作數(shù)棧來完成的。

1.2.2.3.3 動態(tài)鏈接(Dynamic Linking) (或指向運(yùn)行時(shí)常量池的方法引用)

因?yàn)樵诜椒▓?zhí)行的過程中有可能需要用到類中的常量或方法,所以必須要有一個(gè)引用指向運(yùn)行時(shí)常量池。

1.2.2.3.4 方法返回地址(Return Address)(或方法正常退出或者異常退出的定義)

當(dāng)一個(gè)方法執(zhí)行完畢之后,要返回之前調(diào)用它的地方,因此在棧幀中必須保存一個(gè)方法返回地址。

1.2.2.3.5 一些附加信息

例如和調(diào)試相關(guān)的信息,這部分信息完全取決于不同的虛擬機(jī)實(shí)現(xiàn)。

1.2.3 本地方法棧

  1. Java 虛擬機(jī)棧管理 java 方法的調(diào)用,而本地方法棧用于管理本地方法的調(diào)用。

  2. 本地方法棧也是線程私有的.

  3. 允許被實(shí)現(xiàn)成固定或者是可動態(tài)擴(kuò)展的內(nèi)存大小。

內(nèi)存溢出方面也是相同的。
如果線程請求分配的棧容量超過本地方法棧允許的最大容量,會拋出StackOverflowError。
如果本地方法可以動態(tài)擴(kuò)展,并在擴(kuò)展時(shí)無法申請到足夠的內(nèi)存會拋出OutOfMemoryError。

  1. 本地方法是用 C 語言寫的.

1.2.4 堆內(nèi)存

堆內(nèi)存概述:

  1. 一個(gè) JVM 實(shí)例只存在一個(gè)堆內(nèi)存,堆也是 Java 內(nèi)存管理的核心區(qū)域。
  2. Java 堆區(qū)在 JVM 啟動時(shí)的時(shí)候即被創(chuàng)建,其空間大小也就確定了,是 JVM 管理的最大一塊內(nèi)存空間。
  3. 堆內(nèi)存的大小是可以調(diào)節(jié)。一般情況可以將起始值和最大值設(shè)置為一致,這樣會減少垃圾回收之后堆內(nèi)存重新分配大小的次數(shù),提高效率。
  4. 《Java 虛擬機(jī)規(guī)范》規(guī)定,堆可以處于物理上不連續(xù)的內(nèi)存空間中,但邏輯上它應(yīng)該被視為連續(xù)的。
  5. 所有的線程共享 Java 堆,在這里還可以劃分線程私有的緩沖區(qū)。
  6. 《Java 虛擬機(jī)規(guī)范》中對 Java 堆的描述是:所有的對象實(shí)例都應(yīng)當(dāng)在運(yùn)行時(shí)分配在堆上。
  7. 在方法結(jié)束后,堆中的對象不會馬上被移除,僅僅在垃圾收集的時(shí)候才會被移除。
  8. 堆是 GC(Garbage Collection,垃圾收集器)執(zhí)行垃圾回收的重點(diǎn)區(qū)域。

我們在之后在細(xì)講堆內(nèi)存的區(qū)域劃分及垃圾回收機(jī)制

1.2.5方法區(qū)

方法區(qū),是一個(gè)被線程共享的內(nèi)存區(qū)域。其中主要存儲加載的類字節(jié)碼、class/method/field 等元數(shù)據(jù)、static final 常量、static 變量、編譯器編譯后的代碼等數(shù)據(jù)。另外,方法區(qū)包含了一個(gè)特殊的區(qū)域“運(yùn)行時(shí)常量池”。
Java 虛擬機(jī)規(guī)范中明確說明:”盡管所有的方法區(qū)在邏輯上是屬于堆的一部分,但對于HotSpotJVM 而言,方法區(qū)還有一個(gè)別名叫做 Non-Heap(非堆),目的就是要和堆分開。所以,方法區(qū)看做是一塊獨(dú)立于 Java 堆的內(nèi)存空間。方法區(qū)在 JVM 啟動時(shí)被創(chuàng)建,并且它的實(shí)際的物理內(nèi)存空間中和 Java 堆區(qū)一樣都可以是不連續(xù)的。
方法區(qū)的大小:跟堆空間一樣,可以選擇固定大小或者可擴(kuò)展。
方法區(qū)的大小決定了系統(tǒng)可以保存多少個(gè)類,如果系統(tǒng)定義了太多的類,導(dǎo)致方法區(qū)溢出,虛擬機(jī)同樣會拋出內(nèi)存溢出的錯(cuò)誤 :java.lang.OutOfMemoryError:Metaspace。

結(jié)語

這次就先寫這些,文中如果存在不對的地方,歡迎各位讀者批評指正。我會在今后更新本地方法接口執(zhí)行引擎垃圾回收機(jī)制等相關(guān)內(nèi)容,如果大家感興趣,可以關(guān)注博主,我們一起交流學(xué)習(xí)。

本次的分享到這里就結(jié)束了,碼字不易,如果喜歡,賞個(gè)點(diǎn)贊+評論+收藏🤞🤞🤞,感謝您的支持

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
JVM 詳解,大白話帶你認(rèn)識 JVM
JVM總結(jié) ----JVM體系結(jié)構(gòu)
面試阿里高級架構(gòu)師之JVM篇
JVM運(yùn)行原理詳解
JVM專欄
深入了解java虛擬機(jī)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

主站蜘蛛池模板: 微山县| 大同县| 广元市| 潞西市| 新建县| 武功县| 栾城县| 长乐市| 上虞市| 汤阴县| 文登市| 临朐县| 罗山县| 湖南省| 墨竹工卡县| 信宜市| 宜宾县| 石渠县| 桂平市| 卫辉市| 阿巴嘎旗| 康平县| 河西区| 焉耆| 河南省| 绩溪县| 巢湖市| 馆陶县| 大荔县| 新野县| 新巴尔虎右旗| 宁津县| 长岭县| 玛多县| 丹江口市| 朝阳县| 宕昌县| 长葛市| 比如县| 赣州市| 大同县|