JAVA內(nèi)存區(qū)域
JVM在執(zhí)行Java程序的過程中會把它管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域,包括程序計(jì)數(shù)器、Java虛擬機(jī)棧、本地方法棧、Java堆、方法區(qū)等。前三者為線程私有的,后兩者為線程共享區(qū)域。

1. 程序計(jì)數(shù)器(Program Counter Register)
程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼行號指示器。
因?yàn)镴VM的多線程是通過線程輪換并分配處理器執(zhí)行時(shí)間來實(shí)現(xiàn)的,所以在任意時(shí)刻,一個處理器都只會執(zhí)行一條線程中的指令。所以為了切換后能夠恢復(fù)到正確的執(zhí)行位置,每條線程都需要一個獨(dú)立的計(jì)數(shù)器,它們互不影響。因此,程序計(jì)數(shù)器是線程私有的。
如果線程執(zhí)行的是一個Java方法,計(jì)數(shù)器記錄正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址。如果是Native方法,能為空。
2. Java虛擬機(jī)棧(Java Virtual Machine Stacks)
Java虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型: 每個方法執(zhí)行的時(shí)候會創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。一個方法從執(zhí)行到返回,就對應(yīng)一個棧幀在虛擬機(jī)棧中入棧和出棧的過程。
局部變量表
局部變量表存放的是編譯期可知的各種基本數(shù)據(jù)類型、對象引用(reference類型)、returnAddress類型。其中64位長度的long和double類型數(shù)據(jù)會占用2個局部變量空間(Slot),其余只占用1個。局部變量表所需空間在變異期間完成分配,所以當(dāng)進(jìn)入一個方法時(shí),這個方法需要在幀中分配多大的局部變量空間完全確定,運(yùn)行期間不會改變。
3. 本地方法棧(Native Method Stack)
本地方法棧和虛擬機(jī)棧的作用非常相似、區(qū)別在于一個執(zhí)行的是Java方法,一個執(zhí)行的是Native方法。
使用native關(guān)鍵字表示的方法是原生函數(shù),也就是這個方法是用C/C++語言實(shí)現(xiàn)的,并且被編譯成了DLL,由Java去調(diào)用。
4. Java堆(Java Heap)
Java堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動時(shí)創(chuàng)建。這個內(nèi)存區(qū)域的唯一目的就是存放對象實(shí)例以及數(shù)組。Java堆是垃圾收集器的主要區(qū)域。從內(nèi)存回收的角度來看,現(xiàn)代收集器都是使用分代收集算法,所以可以細(xì)分為:新生代和老年代,新生代還可以分為Eden空間、From Survivor空間、To Survivor空間等。
Java堆的JVM參數(shù)主要是 -Xms和-Xmn。
5. 方法區(qū)(Method Area)
方法區(qū)和Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,它被用于存儲已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯期編譯后的代碼等數(shù)據(jù)。
運(yùn)行時(shí)常量池(Runtime Constant Pool)
運(yùn)行時(shí)常量池是方法區(qū)的一部分。 Class文件中除了類的版本、字段、方法、接口等描述信息,還有常量池(Constant Pool Table),主要包括編譯期生成的各種字面量(Literal)和符號引用量(Symbolic References)。字面量相當(dāng)于Java語言層面常量的概念,如文本字符串,聲明為final的常量值等,符號引用則屬于編譯原理方面的概念,符號引用以一組符號來描述所引用的目標(biāo),符號可以是任何形式的字面量,只要使用時(shí)能夠無歧義的定位到目標(biāo)即可。包括了如下三種類型的常量:
- 類和接口的全限定名
- 字段名稱和描述符
- 方法名稱和描述符
這部分內(nèi)容在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池存放。
運(yùn)行時(shí)常量池相對于CLass文件常量池的另外一個重要特征是具備動態(tài)性,Java語言并不要求常量一定只有編譯期才能產(chǎn)生,也就是并非預(yù)置入CLass文件中常量池的內(nèi)容才能進(jìn)入方法區(qū)運(yùn)行時(shí)常量池,運(yùn)行期間也可能將新的常量放入池中,這種特性被開發(fā)人員利用比較多的就是String類的intern()方法。
常量池的好處
常量池是為了避免頻繁的創(chuàng)建和銷毀對象而影響系統(tǒng)性能,其實(shí)現(xiàn)了對象的共享。
例如字符串常量池,在編譯階段就把所有的字符串文字放到一個常量池中。 - 節(jié)省內(nèi)存空間:常量池中所有相同的字符串常量被合并,只占用一個空間。
- 節(jié)省運(yùn)行時(shí)間:比較字符串時(shí),==比equals()快。對于兩個引用變量,只用==判斷引用是否相等,也就可以判斷實(shí)際值是否相等。
雙等號==的含義 - 基本數(shù)據(jù)類型之間應(yīng)用雙等號,比較的是他們的數(shù)值。
- 復(fù)合數(shù)據(jù)類型(類)之間應(yīng)用雙等號,比較的是他們在內(nèi)存中的存放地址。