Java內(nèi)存結(jié)構(gòu)
? ? ? ?顧名思義,主要是關(guān)于Java的運(yùn)行環(huán)境JVM的內(nèi)存功能劃分的概念,一般提到Java內(nèi)存結(jié)構(gòu),主要就是說JVM的內(nèi)存結(jié)構(gòu)。隨著JDK版本的迭代更新,JVM內(nèi)部的結(jié)構(gòu)也在有著不同程度的更新,如JDK7中的Oracle HotSpot 開始移除永久代,JDK8中,永久代被元空間(MetaSpace)所取代等等。所以說Java的內(nèi)存結(jié)構(gòu),主要也是說的這方面的概念。它與Java具體的程序運(yùn)行分析無關(guān),主要是說明了JVM的各個(gè)組成部分以及各組成部分的具體作用是什么。
? ? ? ?Java的內(nèi)存結(jié)構(gòu)可以分為堆內(nèi)內(nèi)存和堆外內(nèi)存。簡單來說,堆內(nèi)內(nèi)存是Java程序運(yùn)行時(shí)的主要使用空間,它受Java虛擬機(jī)的內(nèi)存管理所控制,對(duì)于Java開發(fā)人員來說,它是透明的,開發(fā)人員無法準(zhǔn)確控制。但是可以通過一些手段進(jìn)行調(diào)控,從而使程序性能的性能得以提升。堆外內(nèi)存是指JVM以外的區(qū)域,它不受虛擬機(jī)的內(nèi)存管理所控制,而是有操作系統(tǒng)直接管理。
堆內(nèi)內(nèi)存
? ? ? ?這塊主要是JVM的內(nèi)部組成結(jié)構(gòu),如方法區(qū)、堆區(qū)、棧區(qū)、垃圾回收系統(tǒng)等等。不同的區(qū)有不同功能,具體功能這里不再贅述,其中占據(jù)最大空間的就是堆區(qū),提到堆區(qū),就不得不提堆區(qū)的劃分:新生代、老年代(這里將永久代也劃分為一種特殊的老年代)。不同的區(qū)存儲(chǔ)不同“年齡”的對(duì)象,JVM的垃圾回收機(jī)制,可以根據(jù)不同的對(duì)象區(qū)采用不同的垃圾回收策略。
堆外內(nèi)存
? ? ? ?JVM的內(nèi)存管理雖然帶來了使用的方便,同時(shí)也帶來了降低程序性能的問題,這個(gè)也是Java一直為大眾所詬病的問題。因?yàn)楦鞣N算法就算再精確,也不可能做到及時(shí)有效地回收無用的內(nèi)存,而且在垃圾回收的時(shí)候,甚至?xí)斐善渌ぷ鞯臅和#⊿top the World),這樣會(huì)嚴(yán)重影響系統(tǒng)的整體性能。另外,對(duì)于堆內(nèi)的內(nèi)容在flush到遠(yuǎn)程時(shí),它會(huì)先復(fù)制到直接內(nèi)存(即堆外內(nèi)存),然后再發(fā)送,這對(duì)性能也有一定的影響。
? ? ? ?Java引入堆外內(nèi)存,它就解決了上面的兩個(gè)痛點(diǎn),首先是垃圾回收問題,堆外內(nèi)存的垃圾回收不是JVM來完成的,需要開發(fā)者自己來管理,具體什么時(shí)間進(jìn)行回收,由開發(fā)人員自己決定,這樣避免的垃圾回收的時(shí)機(jī)不準(zhǔn)的問題;但是它同樣帶來了挑戰(zhàn),它對(duì)開發(fā)人員的水平有一定要求,而且一旦出現(xiàn)問題,一般很難排查;然后是flush到遠(yuǎn)程時(shí)的情況,這里因?yàn)樗旧硎褂玫木褪侵苯觾?nèi)存,省去了復(fù)制的步驟,所以堆外內(nèi)存的操作一般效率比較高。
? ? ? ?其他內(nèi)容不再贅述,具體可參考: JVM內(nèi)存結(jié)構(gòu)總結(jié)
Java內(nèi)存模型
概念
? ? ? ?Java的內(nèi)存模型就是描述Java程序中各個(gè)變量(實(shí)例域、靜態(tài)域和數(shù)組元素)之間的關(guān)系,以及在系統(tǒng)中對(duì)變量的存儲(chǔ)和取出操作的具體底層細(xì)節(jié)。它主要針對(duì)的是Java在運(yùn)行期間,數(shù)據(jù)在內(nèi)存中的流轉(zhuǎn)過程的具體實(shí)現(xiàn)細(xì)節(jié)。不涉及具體的物理結(jié)構(gòu),是一種邏輯上的運(yùn)行規(guī)則。
線程工作內(nèi)存
? ? ? ?而Java的內(nèi)存結(jié)構(gòu)一般都直接并發(fā)相關(guān)聯(lián)。一般Java在工作時(shí),內(nèi)部線程的工作空間有兩部分:線程的本地工作空間(也是線程的數(shù)據(jù)緩存空間)和公共共享空間(主內(nèi)存)。線程間的通訊是通過將數(shù)據(jù)刷到主內(nèi)存中來完成的。主內(nèi)存區(qū)域是線程間共享的區(qū)域,任何線程都可以訪問,而本地工作空間是線程私有空間,線程之間不可互相訪問。
SR-133內(nèi)存模型
? ? ? ?SR -133內(nèi)存模型,這是新的Java內(nèi)存模型標(biāo)準(zhǔn)(JMM)。它主要的目的就是為了闡述基于內(nèi)存操作時(shí),進(jìn)程(線程)之間的可見性原則 。之前老版本的Java內(nèi)存模型存在很大的爭(zhēng)議性,也有很多嚴(yán)重的問題,所以JDK5以后引入了SR-133內(nèi)存模型。
重排序
? ? ? ?同時(shí)提到內(nèi)存模型就不得不了解重排序概念。它是編譯器以及處理器內(nèi)部的優(yōu)化過程,也就是說開發(fā)人員寫出的代碼與真正編譯后執(zhí)行的指令順序可能是不一樣的,但是它能保證在單線程下程序的執(zhí)行結(jié)果是不會(huì)發(fā)生變化的。這些對(duì)Java開發(fā)人員是透明的,但是如果不了解它,出了問題就很難深究其具體原因。
Volatile和Synchronized
? ? ? ?另外提到并發(fā),就需要了解Java在并發(fā)環(huán)境下保證執(zhí)行語句的原子性和可見性方式:volatile(不確保原子性,確保可見性)和synchronized(可以確保原子性和可見性)。
? ? ? ?其它內(nèi)容就不再贅述,具體可參考:Java內(nèi)存模型
Java對(duì)象模型
概念
? ? ? ?Java的對(duì)象模型說的是Java中的對(duì)象在內(nèi)存中的存儲(chǔ)結(jié)構(gòu)。它描述的是內(nèi)存中Java對(duì)象的內(nèi)部組成結(jié)構(gòu),相比較于前面兩個(gè)“宏觀”概念,它屬于“微觀”視角。在內(nèi)存中,一個(gè)Java對(duì)象包含三部分:對(duì)象頭、實(shí)例數(shù)據(jù)和對(duì)齊填充。 其中對(duì)象頭是一個(gè)很關(guān)鍵的部分,因?yàn)?strong>對(duì)象頭中包含鎖狀態(tài)標(biāo)志、線程持有的鎖等標(biāo)志 。下面基于HotSpot虛擬機(jī)來分析Java對(duì)象模型。
OOP-Klass Model
? ? ? ?HotSpot是基于C++實(shí)現(xiàn)的。這里的OOP指的是普通對(duì)象指針(Ordinary Object Pointer)。OOP-Klass結(jié)構(gòu)中包含的內(nèi)容很多,大都涉及到C++的具體實(shí)現(xiàn)。這里只需記住一個(gè)結(jié)果:在Java程序運(yùn)行過程中,每創(chuàng)建一個(gè)新的對(duì)象,在JVM內(nèi)部就會(huì)相應(yīng)地創(chuàng)建一個(gè)對(duì)應(yīng)類型的OOP對(duì)象。oop在C++中定義的類型為oopDesc類型,oopDesc是所有OOPS類的共同基類,例如:instanceOopDesc,arrayOopDesc等等,它們的基類都是oopDesc。不同的子類有不同的使用場(chǎng)景。如:instanceOopDesc表示對(duì)象實(shí)例,即當(dāng)我們new一個(gè)對(duì)象的時(shí)候,JVM會(huì)創(chuàng)建一個(gè)instanceOopDesc對(duì)象來表示這個(gè)Java對(duì)象;同理,當(dāng)我們new一個(gè)Java數(shù)組的時(shí)候,JVM會(huì)創(chuàng)建一個(gè)arrayOopDesc對(duì)象來表示這個(gè)數(shù)組對(duì)象。還有其他各種類型:
//定義了oops共同基類
typedef class oopDesc* oop;
//表示一個(gè)Java類型實(shí)例
typedef class instanceOopDesc* instanceOop;
//表示一個(gè)Java方法
typedef class methodOopDesc* methodOop;
//表示一個(gè)Java方法中的不變信息
typedef class constMethodOopDesc* constMethodOop;
//記錄性能信息的數(shù)據(jù)結(jié)構(gòu)
typedef class methodDataOopDesc* methodDataOop;
//定義了數(shù)組OOPS的抽象基類
typedef class arrayOopDesc* arrayOop;
//表示持有一個(gè)OOPS數(shù)組
typedef class objArrayOopDesc* objArrayOop;
//表示容納基本類型的數(shù)組
typedef class typeArrayOopDesc* typeArrayOop;
//表示在Class文件中描述的常量池
typedef class constantPoolOopDesc* constantPoolOop;
//常量池告訴緩存
typedef class constantPoolCacheOopDesc* constantPoolCacheOop;
//描述一個(gè)與Java類對(duì)等的C++類
typedef class klassOopDesc* klassOop;
//表示對(duì)象頭
typedef class markOopDesc* markOop;
? ? ? ?在HotSpot中,oopDesc、instanceOopDesc和arrayOopDesc分別是定義在不同.hpp文件中的(oopDesc定義在oop.hpp中,instanceOopDesc 定義在instanceOop.hpp中,arrayOopDesc定義在arrayOop.hpp中)。
class oopDesc {
friend class VMStructs;
private:
volatile markOop _mark;
union _metadata {
wideKlassOop _klass;
narrowOop _compressed_klass;
} _metadata;
private:
// field addresses in oop
void* field_base(int offset) const;
jbyte* byte_field_addr(int offset) const;
jchar* char_field_addr(int offset) const;
jboolean* bool_field_addr(int offset) const;
jint* int_field_addr(int offset) const;
jshort* short_field_addr(int offset) const;
jlong* long_field_addr(int offset) const;
jfloat* float_field_addr(int offset) const;
jdouble* double_field_addr(int offset) const;
address* address_field_addr(int offset) const;
}
class instanceOopDesc : public oopDesc {
}
class arrayOopDesc : public oopDesc {
}
? ? ? ?實(shí)際上instanceOopDesc里面只是單純地繼承了oopDesc,里面并沒有定義其他的數(shù)據(jù)結(jié)構(gòu)。而從oopDesc源碼中可以發(fā)現(xiàn),它里面實(shí)際可以分成三塊:markOop _mark、共用體 _metadata以及各種field字段。
? _mark:“標(biāo)記”的意思,它里面存儲(chǔ)了相關(guān)對(duì)象的鎖信息,GC分代信息等。
? field:都是各種在oop中定義的字段內(nèi)存地址,都有各自的使用方式,這里不做深入分析。
? _metadata:它里面有兩個(gè)內(nèi)容,_klass(表示普通指針)、_compressed_klass(是壓縮類指針)。
這里主要分析klass
Klass
和oopDesc所處的地位一樣,Klass類是其他Klass類型的基類。它有兩個(gè)功能:
- 實(shí)現(xiàn)語言層面的Java類(在Klass基類中已經(jīng)實(shí)現(xiàn))
- 實(shí)現(xiàn)Java對(duì)象的分發(fā)功能(由Klass的子類提供虛函數(shù)實(shí)現(xiàn))
? ? ? ?其實(shí)由此可以推斷出,實(shí)際上在Java中的類在底層實(shí)現(xiàn)上也是一種對(duì)象,也就是Klass所對(duì)應(yīng)的實(shí)例。例如:instanceKlass類型,它實(shí)際上就是在Java中,每加載一個(gè)類,都會(huì)創(chuàng)建一個(gè)instanceKlass對(duì)象,所以在底層實(shí)現(xiàn)上也可以說類也是一種“對(duì)象”,只不過是一種特殊的對(duì)象。
? ? ? ?基于Java類也是一種“對(duì)象”的觀點(diǎn),自然可以想到:既然也是對(duì)象,那么在JVM中,它的存儲(chǔ)方式實(shí)際上與對(duì)象實(shí)例的存儲(chǔ)方式是相類似的,它也有對(duì)應(yīng)的oop,只不過是klassOop,而且如果是oop結(jié)構(gòu),那么肯定也會(huì)存在相對(duì)應(yīng)的一個(gè)klass來描述(klassKlass )。klassKlass也是klass子類的一種。具體結(jié)構(gòu)可以參考下圖:

? 這里有一個(gè)很好的例子:
class Model {
public static int a = 1;
public int b;
public Model(int b) {
this.b = b;
}
}
public static void main(String[] args) {
int c = 10;
Model modelA = new Model(2);
Model modelB = new Model(3);
}
? ? ? ?具體存儲(chǔ)結(jié)構(gòu)如下圖:
? ? ? ?具體對(duì)象模型分析可以參考博客:深入理解多線程(二)--- Java對(duì)象模型,深入理解多線程(三)--- Java的對(duì)象頭
總結(jié):
? ? ? ?在JVM加載類的時(shí)候,會(huì)創(chuàng)建一個(gè)instanceKlass,因?yàn)镴VM中類加載信息存儲(chǔ)在方法區(qū)中,所以instanceKlass會(huì)在方法區(qū)中創(chuàng)建。當(dāng)我們?cè)贘ava中new一個(gè)對(duì)象的時(shí)候,無論是普通對(duì)象還是數(shù)組,都是對(duì)象的一種,它會(huì)在JVM中創(chuàng)建一個(gè)對(duì)應(yīng)的instanceOopDesc對(duì)象,來表示Java中的對(duì)象,它里面主要結(jié)構(gòu)有三部分:_mark部分(用于存儲(chǔ)對(duì)象的鎖信息,GC分代信息等)、_metadata(共用體,存儲(chǔ)一個(gè)普通klass指針,指向?qū)ο笏鶎兕?,_compressed_kass,壓縮類的指針)。而且在JVM中,實(shí)際上類在底層實(shí)現(xiàn)上也是一種對(duì)象,它也有與Java對(duì)象在底層實(shí)現(xiàn)上相類似的結(jié)構(gòu),也會(huì)有一個(gè)klass來描述所屬類型,所以不難想象出,這里的klass實(shí)際上就形成了一種鏈?zhǔn)浇Y(jié)構(gòu)。