深入理解JVM_內存管理對象訪問與大小02
阿新 • • 發佈:2017-06-24
gms uwa hint map awd bpp ase pbo cdc
1、對象訪問:
在java語言中,對象訪問如何進行的?
(1)最簡單的訪問,也會涉及java棧、java堆和方法區這三個最重要的內存區域之間的關聯關系。
Object obj = new Object();
<1> “Object obj”:反應到java棧的本地變量表中,作為一個reference類型數據出現。
<2> “new Object()”:反應到java堆中,形成一塊存儲了Object類型所有實例數據值的結構化內存。這塊內存的長度不是固定的。
<3> java堆中還必須包含能查找到此對象類型數據(如對象類型、父類、實現的接口、方法等)的地址信息,這些類型數據則存儲在方法區中。
(2)由於reference類型在java虛擬機規範裏只規定了一個指向對象的引用,並沒有定義這個引用應該通過哪種方式去定位,以及訪問到java堆中的對象的具體位置。主流的訪問方式有兩種:
<1> 使用句柄;
Java堆中將會劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,此句柄中包含了:“對象實例數據”和“類型數據”各自具體的地址信息。具體如下圖:
<2> 直接指針。
Java堆對象的布局中就必須考慮如何放置訪問類型數據的相關信息,reference中直接存儲的就是對象地址。如下圖:
<3> 兩種方式的優缺點:
(1) 句柄優點:reference中存儲的是穩定的句柄地址,在對象被移動(垃圾收集時移動對象是非常普遍的行為)時只會改變句柄中的實例數據指針,而reference本身不需要被改變。
(2) 直接指針優點:速度很快,它節省一次指針定位的時間開銷。Sun HotSpot而言,就是使用此種方式。
2、Java對象的大小:
基本數據的類型的大小是固定的。非基本類型的java對象,其大小就值得商榷了。
在java中,一個空object對象的大小是8byte。這個大小只是保存堆中一個沒有任何屬性的對象的大小。如下語句:
Object obj = new Object();
這樣在程序中完成了一個java對象的生命,但是它所占的空間為:4byte+8byte。期中4byte就是上面所說的java棧中保存引用的所需要的空間,而8byte則是java堆中對象的信息。因為java中非基本類型的對象都需要繼承Object對象,所有不論什麽樣的java對象,其大小都必須大於8byte。
有了Object對象的大小,我們就可以計算其他對象的大小了。
Class NewObject {
int count;
boolean flag;
Object ob;
}
其大小為:空對象大小(8byte)+int大小(4byte)+Boolean大小(1byte)+空Object引用的大小
(4byte)=17byte。但是因為Java在對對象內存分配時都是以8的整數倍來分,因此大於17byte的最接
近8的整數倍的是24,因此此對象的大小為24byte。
程序計數器、虛擬機棧、本地方棧3個區域隨線程而生,隨線程而滅,棧中的棧幀隨著方法的進入和退出而執行著出棧和入棧操作。每一個棧幀中分配多少內存基本上是在類結構確定下來時就已知的。
以下內容待定:
2、內存分配
java對象所占用的內存主要從堆上進行分配,堆是所有線程共享的,因此堆上分配內存時需要進行加鎖,導致了創建對象開銷比較大。當堆上空間不足時,會觸發GC,如果GC後空間仍然不足,則出OutOfMemory錯誤信息。
JDK提升內存分配的效率,會為每個新創建的線程在新生代的Eden Space上分配一塊獨立的空間。這個空間稱為TLAB(Thread Local Allocation Buffer),其大小有JVM根據運行情況計算而得。
可通過:-XX:TLABwasteTargetPercent來設置TLAB可占用的Eden Space的百分比,默認值為1%。
JVM將根據這個比率,線程數量及線程是否頻繁分配對象來給每個線程分配合適大小的TLAB空間。在TLAB上分配內存時不需要加鎖,因此JVM在給線程中的對象分配內存時會盡量在TLAB上分配,如果對象過大或TLAB空間已用完,則仍然在堆上進行分配。
因此在編寫java程序時,通常多個小的對象比大的對象分配起來更加高效。可通過在啟動參數上增加-XX:+PrintTLAB來查看TLAB空間的使用情況。
除了從堆上分配及從TLAB上分配外,還有一種基於逃逸分析直接在棧上進行分配的方式。
深入理解JVM_內存管理對象訪問與大小02