個(gè)人主頁:http://shiyiliang.cn
Java內(nèi)存結(jié)構(gòu)
Java虛擬機(jī)會將內(nèi)存分為幾個(gè)不同的管理區(qū),這些區(qū)域各自有各自的用途,根據(jù)不同的特點(diǎn),承擔(dān)不同的任務(wù)以及在垃圾回收時(shí)運(yùn)用不同的 算法 ??傮w分為下面幾個(gè)部分:
**程序計(jì)數(shù)器(Program Counter Register) ** 、 ** JVM虛擬機(jī)棧(JVM Stacks ) ** 、 ** 本地方法棧(Native Method Stacks) ** 、 ** 堆(Heap) ** 、 ** 方法區(qū)(Method Area) **
如下圖:
1、程序計(jì)數(shù)器(Program Counter Register)
這是一塊比較小的內(nèi)存,不在Ram上,而是直接劃分在CPU上的,程序員無法直接操作它,它的作用是:JVM在解釋字節(jié)碼文件(.class)時(shí),存儲當(dāng)前線程所執(zhí)行的字節(jié)碼的行號,只是一種概念模型,各種JVM所采用的方式不同,字節(jié)碼解釋器工作時(shí),就是通過改變程序計(jì)數(shù)器的值來選取下一條要執(zhí)行的指令,分支、循環(huán)、跳轉(zhuǎn)、等基礎(chǔ)功能都是依賴此技術(shù)區(qū)完成的。還有一種情況,就是我們常說的Java多線程方面的,多線程就是通過現(xiàn)程輪流切換而達(dá)到的,同一時(shí)刻,一個(gè)內(nèi)核只能執(zhí)行一個(gè)指令,所以,對于每一個(gè)程序來說,必須有一個(gè)計(jì)數(shù)器來記錄程序的執(zhí)行進(jìn)度,這樣,當(dāng)現(xiàn)程恢復(fù)執(zhí)行的時(shí)候,才能從正確的地方開始,所以,每個(gè)線程都必須有一個(gè)獨(dú)立的程序計(jì)數(shù)器,這類計(jì)數(shù)器為線程私有的內(nèi)存。如果一個(gè)線程正在執(zhí)行一個(gè)Java方法,則計(jì)數(shù)器記錄的是字節(jié)碼的指令的地址,如果執(zhí)行的一個(gè)Native方法,則計(jì)數(shù)器的記錄為空,此內(nèi)存區(qū)是唯一一個(gè)在Java規(guī)范中沒有任何OutOfMemoryError情況的區(qū)域。
2、JVM虛擬機(jī)棧(JVM Stacks)
JVM虛擬機(jī)棧就是 ** 我們常說的堆棧的棧(我們常常把內(nèi)存粗略分為堆和棧) ** ,和程序計(jì)數(shù)器一樣,也是線程私有的,生命周期和線程一樣,每個(gè)方法被執(zhí)行的時(shí)候會產(chǎn)生一個(gè) ** 棧幀 ** ,用于存儲局部變量表、動態(tài)鏈接、操作數(shù)、方法出口等信息。方法的執(zhí)行過程就是棧幀在JVM中出棧和入棧的過程。 ** 局部變量表中存放的是各種基本數(shù)據(jù)類型 ,如boolean、byte、char、等8種 ,及引用類型(存放的是指向各個(gè)對象的內(nèi)存地址) ** ,因此,它有一個(gè)特點(diǎn):內(nèi)存空間可以在編譯期間就確定,運(yùn)行期不在改變。這個(gè)內(nèi)存區(qū)域會有兩種可能的Java異常:StackOverFlowError和OutOfMemoryError。
3、本地方法棧(Native Method Stacks)
從名字即可看出,本地方法棧就是用來處理Java中的本地方法的,Java類的祖先類Object中有眾多Native方法,如hashCode()、wait()等,他們的執(zhí)行很多時(shí)候是借助于 操作系統(tǒng) ,但是JVM需要對他們做一些規(guī)范,來處理他們的執(zhí)行過程。此區(qū)域,可以有不同的實(shí)現(xiàn)方法,向我們常用的Sun的JVM就是本地方法棧和JVM虛擬機(jī)棧是同一個(gè)。
4、堆(Heap)
堆內(nèi)存是內(nèi)存中最重要的一塊,也是最有必要進(jìn)行深究的一部分。 ** 因?yàn)镴ava性能的優(yōu)化,主要就是針對這部分內(nèi)存的 ** 。所有的 ** 對象實(shí)例及數(shù)組 ** 都是在堆上面分配的(隨著JIT技術(shù)的逐漸成熟,這句話視乎有些絕對,不過至少目前還基本是這樣的),可 通過-Xmx和-Xms來控制堆的大小。 JIT技術(shù)的發(fā)展產(chǎn)生了新的技術(shù),如棧上分配和標(biāo)量替換,也許在不久的幾年里,即時(shí)編譯會誕生及成熟,那個(gè)時(shí)候,“所有的對象實(shí)例及數(shù)組都是在堆上面分配的”這句話就應(yīng)該稍微改改了。堆內(nèi)存是垃圾回收的主要區(qū)域,所以在下文垃圾回收板塊會重點(diǎn)介紹,此處只做概念方面的解釋。在32位系統(tǒng)上最大為2G,64位系統(tǒng)上無限制。可通過-Xms和-Xmx控制,-Xms為JVM啟動時(shí)申請的最小Heap內(nèi)存,-Xmx為JVM可申請的最大Heap內(nèi)存。
5、方法區(qū)(Method Area)
方法區(qū)(又稱為靜態(tài))是所有**線程共享的內(nèi)存區(qū)域 ** ,用于存儲 ** 已經(jīng)被JVM加載的類信息、常量、靜態(tài)變量等數(shù)據(jù) ** ,一般來說,方法區(qū)屬于持久代(關(guān)于持久代,會在GC部分詳細(xì)介紹,除了持久代,還有新生代和舊生代),也難怪Java規(guī)范將方法區(qū)描述為堆的一個(gè)邏輯部分, ** 但是它不是堆 ** 。方法區(qū)的垃圾回收比較棘手,就算是Sun的HotSpot VM在這方面也沒有做得多么完美。此處引入方法區(qū)中一個(gè)重要的概念:運(yùn)行時(shí)常量池。主要用于存放在編譯過程中產(chǎn)生的 ** 字面量(字面量簡單理解就是常量)和引用 ** 。一般情況,常量的內(nèi)存分配在編譯期間就能確定,但不一定全是,有一些可能就是運(yùn)行時(shí)也可將常量放入常量池中,如String類中有個(gè)Native方法intern()<關(guān)于intern()的詳細(xì)說明, 此處補(bǔ)充一個(gè)在JVM內(nèi)存管理之外的一個(gè)內(nèi)存區(qū):直接內(nèi)存。在JDK1.4中新加入類NIO類,引入了一種基于通道與緩沖區(qū)的I/O方式,它可以使用Native函數(shù)庫直接分配堆外內(nèi)存,即我們所說的直接內(nèi)存,這樣在某些場景中會提高程序的性能。
Java內(nèi)存分陪分析實(shí)例
內(nèi)存分配的實(shí)例,可以去看下這篇文章
java中的各種數(shù)據(jù)類型在內(nèi)存中存儲的方式
參考文章: