<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          想搞清楚Java8的內(nèi)存結(jié)構(gòu),看這篇就對了

          共 3458字,需瀏覽 7分鐘

           ·

          2021-04-12 21:42


          java8內(nèi)存結(jié)構(gòu)圖

          虛擬機(jī)內(nèi)存與本地內(nèi)存的區(qū)別

          Java虛擬機(jī)在執(zhí)行的時候會把管理的內(nèi)存分配成不同的區(qū)域,這些區(qū)域被稱為虛擬機(jī)內(nèi)存,同時,對于虛擬機(jī)沒有直接管理的物理內(nèi)存,也有一定的利用,這些被利用卻不在虛擬機(jī)內(nèi)存數(shù)據(jù)區(qū)的內(nèi)存,我們稱它為本地內(nèi)存,這兩種內(nèi)存有一定的區(qū)別:

          JVM內(nèi)存

          • 受虛擬機(jī)內(nèi)存大小的參數(shù)控制,當(dāng)大小超過參數(shù)設(shè)置的大小時就會報OOM

          本地內(nèi)存

          • 本地內(nèi)存不受虛擬機(jī)內(nèi)存參數(shù)的限制,只受物理內(nèi)存容量的限制
          • 雖然不受參數(shù)的限制,但是如果內(nèi)存的占用超出物理內(nèi)存的大小,同樣也會報OOM

          java運(yùn)行時數(shù)據(jù)區(qū)域

          java虛擬機(jī)在執(zhí)行過程中會將所管理的內(nèi)存劃分為不同的區(qū)域,有的隨著線程產(chǎn)生和消失,有的隨著java進(jìn)程產(chǎn)生和消失,根據(jù)《Java虛擬機(jī)規(guī)范》的規(guī)定,運(yùn)行時數(shù)據(jù)區(qū)分為以下一個區(qū)域:

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

          程序計數(shù)器就是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器,通過改變計數(shù)器的值,來選取下一行指令,通過他來實現(xiàn)跳轉(zhuǎn)、循環(huán)、恢復(fù)線程等功能。

          • 在任何時刻,一個處理器內(nèi)核只能運(yùn)行一個線程,多線程是通過線程輪流切換,分配時間來完成的,這就需要有一個標(biāo)志來記住每個線程執(zhí)行到了哪里,這里便需要到了程序計數(shù)器。
          • 所以,程序計數(shù)器是線程私有的,每個線程都已自己的程序計數(shù)器。

          虛擬機(jī)棧(JVM Stacks)

          虛擬機(jī)棧是線程私有的,隨線程生滅。虛擬機(jī)棧描述的是線程中的方法的內(nèi)存模型:

          每個方法被執(zhí)行的時候,都會在虛擬機(jī)棧中同步創(chuàng)建一個棧幀(stack frame)。

          每個棧幀的包含如下的內(nèi)容

          • 局部變量表
            • 局部變量表中存儲著方法里的java基本數(shù)據(jù)類型(byte/boolean/char/int/long/double/float/short)以及對象的引用(注:這里的基本數(shù)據(jù)類型指的是方法內(nèi)的局部變量)
          • 操作數(shù)棧
          • 動態(tài)連接
          • 方法返回地址

          方法被執(zhí)行時入棧,執(zhí)行完后出棧

          虛擬機(jī)棧可能會拋出兩種異常:

          • 如果線程請求的棧深度大于虛擬機(jī)所規(guī)定的棧深度,則會拋出StackOverFlowError即棧溢出
          • 如果虛擬機(jī)的棧容量可以動態(tài)擴(kuò)展,那么當(dāng)虛擬機(jī)棧申請不到內(nèi)存時會拋出OutOfMemoryError即OOM內(nèi)存溢出

          本地方法棧(Native Method Stacks)

          本地方法棧與虛擬機(jī)棧的作用是相似的,都會拋出OutOfMemoryError和StackOverFlowError,都是線程私有的,主要的區(qū)別在于:

          • 虛擬機(jī)棧執(zhí)行的是java方法
          • 本地方法棧執(zhí)行的是native方法(什么是Native方法?)

          Java堆(Java Heap)

          java堆是JVM內(nèi)存中最大的一塊,由所有線程共享,是由垃圾收集器管理的內(nèi)存區(qū)域,主要存放對象實例,當(dāng)然由于java虛擬機(jī)的發(fā)展,堆中也多了許多東西,現(xiàn)在主要有:

          • 對象實例
            • 類初始化生成的對象
            • 基本數(shù)據(jù)類型的數(shù)組也是對象實例
          • 字符串常量池
            • 字符串常量池原本存放于方法區(qū),jdk7開始放置于堆中。
            • 字符串常量池存儲的是string對象的直接引用,而不是直接存放的對象,是一張string table
          • 靜態(tài)變量
            • 靜態(tài)變量是有static修飾的變量,jdk7時從方法區(qū)遷移至堆中
          • 線程分配緩沖區(qū)(Thread Local Allocation Buffer)
            • 線程私有,但是不影響java堆的共性
            • 增加線程分配緩沖區(qū)是為了提升對象分配時的效率

          java堆既可以是固定大小的,也可以是可擴(kuò)展的(通過參數(shù)-Xmx和-Xms設(shè)定),如果堆無法擴(kuò)展或者無法分配內(nèi)存時也會報OOM。

          方法區(qū)(Method Area)

          方法區(qū)絕對是網(wǎng)上所有關(guān)于java內(nèi)存結(jié)構(gòu)文章爭論的焦點,因為方法區(qū)的實現(xiàn)在java8做了一次大革新,現(xiàn)在我們來討論一下:

          方法區(qū)是所有線程共享的內(nèi)存,在java8以前是放在JVM內(nèi)存中的,由永久代實現(xiàn),受JVM內(nèi)存大小參數(shù)的限制,在java8中移除了永久代的內(nèi)容,方法區(qū)由元空間(Meta Space)實現(xiàn),并直接放到了本地內(nèi)存中,不受JVM參數(shù)的限制(當(dāng)然,如果物理內(nèi)存被占滿了,方法區(qū)也會報OOM),并且將原來放在方法區(qū)的字符串常量池和靜態(tài)變量都轉(zhuǎn)移到了Java堆中,方法區(qū)與其他區(qū)域不同的地方在于,方法區(qū)在編譯期間和類加載完成后的內(nèi)容有少許不同,不過總的來說分為這兩部分:

          類元信息(Klass)

          • 類元信息在類編譯期間放入方法區(qū),里面放置了類的基本信息,包括類的版本、字段、方法、接口以及常量池表(Constant Pool Table)
          • 常量池表(Constant Pool Table)存儲了類在編譯期間生成的字面量、符號引用(什么是字面量?什么是符號引用?),這些信息在類加載完后會被解析到運(yùn)行時常量池中

          運(yùn)行時常量池(Runtime Constant Pool)

          • 運(yùn)行時常量池主要存放在類加載后被解析的字面量與符號引用,但不止這些
          • 運(yùn)行時常量池具備動態(tài)性,可以添加數(shù)據(jù),比較多的使用就是String類的intern()方法

          直接內(nèi)存

          直接內(nèi)存位于本地內(nèi)存,不屬于JVM內(nèi)存,但是也會在物理內(nèi)存耗盡的時候報OOM,所以也講一下。

          在jdk1.4中加入了NIO(New Input/Putput)類,引入了一種基于通道(channel)與緩沖區(qū)(buffer)的新IO方式,它可以使用native函數(shù)直接分配堆外內(nèi)存,然后通過存儲在java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進(jìn)行操作,這樣可以在一些場景下大大提高IO性能,避免了在java堆和native堆來回復(fù)制數(shù)據(jù)。

          常見問題

          什么是Native方法?

          由于java是一門高級語言,離硬件底層比較遠(yuǎn),有時候無法操作底層的資源,于是,java添加了native關(guān)鍵字,被native關(guān)鍵字修飾的方法可以用其他語言重寫,這樣,我們就可以寫一個本地方法,然后用C語言重寫,這樣來操作底層資源。當(dāng)然,使用了native方法會導(dǎo)致系統(tǒng)的可移植性不高,這是需要注意的。

          成員變量、局部變量、類變量分別存儲在內(nèi)存的什么地方?

          類變量

          • 類變量是用static修飾符修飾,定義在方法外的變量,隨著java進(jìn)程產(chǎn)生和銷毀
          • 在java8之前把靜態(tài)變量存放于方法區(qū),在java8時存放在堆中

          成員變量

          • 成員變量是定義在類中,但是沒有static修飾符修飾的變量,隨著類的實例產(chǎn)生和銷毀,是類實例的一部分
          • 由于是實例的一部分,在類初始化的時候,從運(yùn)行時常量池取出直接引用或者值,與初始化的對象一起放入堆中

          局部變量

          • 局部變量是定義在類的方法中的變量
          • 在所在方法被調(diào)用時放入虛擬機(jī)棧的棧幀中,方法執(zhí)行結(jié)束后從虛擬機(jī)棧中彈出,所以存放在虛擬機(jī)棧中

          由final修飾的常量存放在哪里?

          final關(guān)鍵字并不影響在內(nèi)存中的位置,具體位置請參考上一問題。

          類常量池、運(yùn)行時常量池、字符串常量池有什么關(guān)系?有什么區(qū)別?

          類常量池與運(yùn)行時常量池都存儲在方法區(qū),而字符串常量池在jdk7時就已經(jīng)從方法區(qū)遷移到了java堆中。

          在類編譯過程中,會把類元信息放到方法區(qū),類元信息的其中一部分便是類常量池,主要存放字面量和符號引用,而字面量的一部分便是文本字符,在類加載時將字面量和符號引用解析為直接引用存儲在運(yùn)行時常量池;

          對于文本字符來說,它們會在解析時查找字符串常量池,查出這個文本字符對應(yīng)的字符串對象的直接引用,將直接引用存儲在運(yùn)行時常量池;字符串常量池存儲的是字符串對象的引用,而不是字符串本身。

          什么是字面量?什么是符號引用?

          字面量

          java代碼在編譯過程中是無法構(gòu)建引用的,字面量就是在編譯時對于數(shù)據(jù)的一種表示:

          int a=1;//這個1便是字面量
          String b="iloveu";//iloveu便是字面量

          符號引用

          由于在編譯過程中并不知道每個類的地址,因為可能這個類還沒有加載,所以如果你在一個類中引用了另一個類,那么你完全無法知道他的內(nèi)存地址,那怎么辦,我們只能用他的類名作為符號引用,在類加載完后用這個符號引用去獲取他的內(nèi)存地址。

          例子:我在com.demo.Solution類中引用了com.test.Quest,那么我會把com.test.Quest作為符號引用存到類常量池,等類加載完后,拿著這個引用去方法區(qū)找這個類的內(nèi)存地址。


          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  综合色中文娱乐网 | 青青草在线视频在线免费 | 骚视频在线观看 | 欧美日韩亚洲中文字幕 | 国产综合第一页 |