Android必學(xué)基礎(chǔ)知識之Java內(nèi)存結(jié)構(gòu)

前言

?JVM在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域。這些區(qū)域都有各自的用途,以及創(chuàng)建和銷毀的時間,有的區(qū)域隨著虛擬機進程的啟動而一直存在,有些區(qū)域則是依賴用戶線程的啟動和結(jié)束而建立與銷毀。
?JVM所管理的內(nèi)存包括以下幾個區(qū)域:

JVM運行時數(shù)據(jù)區(qū)

1 程序計數(shù)器

定義

  • 是一塊較小的內(nèi)存空間,它可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼行號指示器

為什么是線程隔離的數(shù)據(jù)區(qū)?

  • 由于JVM的多線程是通過線程輪流i切換、分配處理器執(zhí)行時間的方式來實現(xiàn)的,在任何一個確定的時刻,一個處理器都只會執(zhí)行一條程序中的指令。因此,為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個獨立的程序計數(shù)器。
  • 如果程序正在執(zhí)行Java方法,計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令地址;如果正在執(zhí)行的是Native方法,計數(shù)器值為空(undefined)

2 Java虛擬機棧(VM Stack)

定義

  • -描述的是Java方法執(zhí)行的線程內(nèi)存模型:每個方法被執(zhí)行的時候,JVM都會同步創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)連接、方法出口等信息。

為什么是線程隔離的數(shù)據(jù)區(qū)?

  • 因為Java虛擬機棧描述的是Java方法執(zhí)行的線程內(nèi)存模型,每個線程執(zhí)行的時間和順序不一定相同,所以棧幀也一定相同。

異常

  • 如果線程請求的棧深度大于虛擬機所允許的深度,拋出StackOverflowError
  • 如果Java虛擬機棧容量可以動態(tài)擴展,當(dāng)棧擴展時無法申請到足夠的內(nèi)存拋OutOfMemoryError。

3 本地方法棧(Native Method Stack)

定義

  • 和Java虛擬機棧相似,描述的是本地方法執(zhí)行的線程內(nèi)存模型

虛擬機異同

  • 虛擬機可以根據(jù)需要自由實現(xiàn)本地方法棧
  • HotSpot虛擬機直接把虛擬機棧和本地方法棧合二為一

4 Java堆

定義

  • 所有的對象實例以及數(shù)組都應(yīng)當(dāng)在堆上分配。

為什么是線程共享的?

  • 因為是內(nèi)存中最大的一塊,并且是垃圾收集器管理的內(nèi)存區(qū)域。

異常

  • 如果在Java堆沒有內(nèi)存完成實例分配,并且堆也無法再擴展時,拋出OutOfMemoryError。

5 方法區(qū)

定義

  • 用于存儲已被虛擬機加載的類型信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼緩存等數(shù)據(jù)。
  • 《Java虛擬機規(guī)范》中把方法區(qū)描述為堆的一個邏輯部分,但是它卻有一個別名叫做非堆(Non-Heap)目的是與Java堆區(qū)分開來。

異常

  • 如果方法無法滿足新的內(nèi)存分配需求時,拋出OutOfMemoryError。

6 運行時常量

定義

  • 方法區(qū)的一部分,用于存放編譯期生成的各種字面量與符號引用,這部分內(nèi)容將在類加載后存放到方法區(qū)的運行時常量池中。

什么是Class常量池?

  • class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用于存放編譯期生成的各種字面量和符號引用,這部分內(nèi)容將在類加載后進入方法區(qū)的運行時常量池中存放。

運行期間能將新的常量放入池嗎?

  • 并非預(yù)置入Class文件中常量池的內(nèi)容才能進入方法區(qū)運行時常量池,運行期間也可能將新的常量放入池中,例如String類的intern方法。

異常

  • 當(dāng)常量池?zé)o法再申請到內(nèi)存時會拋出OutOfMemoryError。

7 直接內(nèi)存

?直接內(nèi)存不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,也不是《Java虛擬機規(guī)范》中定義的內(nèi)存區(qū)域,但是這部分也被頻繁使用。
定義

  • NIO類引入一種基于通道與緩沖區(qū)的I/O方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在Java堆里面的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。
    異常
  • 各個內(nèi)存區(qū)域總和大于物理內(nèi)存限制,從而導(dǎo)致動態(tài)擴展時出現(xiàn)OutOfMemoryError。

8 對象

?以虛擬機HotSpot和常用的內(nèi)存區(qū)域Java堆為例,探討Java堆中對象分配、布局和訪問的全過程。

8.1 創(chuàng)建對象

?創(chuàng)建對象分為以下 四步:
①當(dāng)Java虛擬機遇到一條字節(jié)碼new指令時,首先將去檢查這個指令的參數(shù)是否能在常量池中定位到一個類的符號引用,并檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,則必須執(zhí)行相應(yīng)的類加載過程。
②在類加載檢查通過后,接下來虛擬機將為新生對象分配內(nèi)存。有以下兩種分配方法:

  • 指針碰撞:在規(guī)整的內(nèi)存中,以指針作為使用過的內(nèi)存和空閑內(nèi)存的分界點指示器,把指針向空閑方向挪動一段與對象大小相等的距離。
  • 空閑列表:虛擬機維護一個列表,記錄可用內(nèi)存,分配時從列表中找到一塊合適的空間劃分給對象實例,并更新列表上的記錄。

對象創(chuàng)建在虛擬機中時非常頻繁的行為,但是在并發(fā)情況下也不是線程安全的。解決這個問題有兩種可選方案:
一、虛擬機采用CAS配上失敗重試的方法保證更新操作的原子性;
二、在每個線程Java堆中預(yù)先分配一小塊內(nèi)存,成為本地線程分配緩沖TLAB,哪個線程需要分配內(nèi)存,就在哪個線程的TLAB上分配,只有TLAB用完需要分配新的TLAB才需要同步。

③內(nèi)存分配完成之后,虛擬機必須將分配到的內(nèi)存空間(不包括對象頭)都初始化為零值,如果使用了TLAB,則可提前至TLAB分配時順便進行。之后對對象進行必要的設(shè)置,這些信息將存放在對象的對象頭之中。
④從虛擬機的視角看,一個新的對象已經(jīng)產(chǎn)生。接著執(zhí)行<init>()方法,按照程序員的意愿對對象進行初始化,一個真正的對象才算被完全構(gòu)造出來。

8.2 對象的內(nèi)存布局

?在HotSpot虛擬機里,對象在堆內(nèi)存中國的存儲布局可以劃分為三個部分:對象頭、實例數(shù)據(jù)和對其填充。
對象頭
?HotSpot虛擬機對象的對象頭部分包括兩類信息。

  • 第一類部分用于存儲對象自身運行時數(shù)據(jù)(HashCode、GC分代年齡等等)
  • 第二類是指針類型,即對象指向它的類型元數(shù)據(jù)的指針,通過這個指針確認對象是哪個類的實例。

實例數(shù)據(jù)

  • 對象真正存儲的有效信息,即我們在程序代碼里定義的各種類型的字段內(nèi)容。

對齊填充

  • 占位符,目的是為了保證對象的大小是8字節(jié)的整數(shù)倍。

8.3 對象的訪問定位

?《Java虛擬機規(guī)范》規(guī)定reference只是一個對象的引用,沒有定義引用通過什么方式去定位和訪問堆中對象的位置。主流的訪問方式主要有使用句柄和直接指針兩種:

  • Java堆中將可能劃分出一塊內(nèi)存來作為句柄池,reference中存儲句柄地址,句柄包含了對象的實例數(shù)據(jù)與類型數(shù)據(jù)各自的地址信息。
  • reference中直接存儲對象的地址。

句柄訪問的優(yōu)缺點

  • 優(yōu)點:句柄處于穩(wěn)定位置,內(nèi)存整理時reference不需要被改變(垃圾回收時移動對象是一種常見的現(xiàn)象)。
  • 缺點:多了一次定位開銷

直接指針的優(yōu)缺點

  • 與句柄相反,內(nèi)存整理時reference要改變,但是訪問對象時少了一次指針定位的開銷。


總結(jié)

  • Java內(nèi)存結(jié)構(gòu)可以大致分為由所有線程共享的數(shù)據(jù)區(qū)和線程隔離的數(shù)據(jù)區(qū)。
  • 方法區(qū)也屬于邏輯上的堆,在HotSpot中VM stack和Native Method Stack合并在一起,因此可以大致分為堆棧、程序計數(shù)器。
  • 對象訪問定位的方法各有優(yōu)勢,不同虛擬機實現(xiàn)不同。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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