四、對象如何創建,布局?如何訪問數據
四、對象如何創建,布局?如何訪問數據
對象的內存分配
對象的創建過程
Jvm讀到new指令,
先去方法區(類常量池中)查看是否有對應的類符號,並檢查該類是否被加載,
if 加載,JVM為新生對象分配內存
else 加載類,為對象分配內存
內存分配完,JVM將內存空間值初始化為0值
對象頭信息記錄,這個對象是哪個類的實例,如何找到類元數據信息,對象哈希碼值,對象的GC分代信息記錄到對象頭中;
new之後執行init操作
JVM內存分配的方式
指針碰撞Bump the pointer分配內存
如果堆內存使用是很規整的,如圖,一邊是已使用;一邊是未使用;那分配內存就是把指針向空閑空間那邊移動一段距離,這種分配方式成為指針碰撞
適用範圍:堆內存使用很規整
空閑列表分配內存
JVM必須維護一個內存使用列表,記錄內存使用情況。分配時到列表中找到一個足夠大的內存來劃分給對象實例,並更新列表的記錄。這種分配方法叫空閑列表!
適用範圍:堆內存使用散亂,不規整!
選擇
在使用Serial、 ParNew等帶Compact過程的收集器時,系統采用的分配算法是指針碰撞,否則,使用CMS這種基於Mark-Sweep算法的收集器時,通常采用空閑列表。
並發創建對象時的內存分配問題
2個線程,1個指針,a線程分配的A對象的內存可能沒分配完,b線程分配B對象內存時卻發現a線程移動了內存中的指針!就會產生並發問題!如何解決?
兩個方案:
1)分配內存空間的動作做同步處理
2)把內存分配的動作按照線程劃分在不同空間之中執行
每個線程在java堆中預先分配一小塊內存,稱為本地線程緩沖區Thread Local Allocation Buffer-TLAB;哪個線程要分配內存,就在TLAB上分配。TLAB用完,並重新分配新的TLAB時,才需要同步!
虛擬機是否使用TLAB,可以通過-XX:+/-UseTLAB 參數設定
Java對象在內存中的結構
HotSpot虛擬機中,對象在堆內存中存儲,結構分別是:
1)對象頭(Header)
markword:
長度:依計算機位數而定,
32/64位計算機的markword分別為32bit(4字節)和64bit(8字節);
Class對象指針:對象指向它所屬類的元數據的指針;
2)實例數據(Instance Data)
3)對齊填充(Padding)。占位符,讓對象的大小必須是8字節的整數倍。對象頭部分正好是8字節的倍數(1倍或者2倍);因此當對象實例數據部分沒有對齊時,就需要對齊填充來補全。
http://www.cnblogs.com/duanxz/p/4967042.html
一個對象占多大內存空間?
對象占用空間是8字節對齊的
對象頭:32位 4字節/ 64位 8字節
Class對象指針: 32位 4字節/ 64位 8字節?
實例數據:按照基本類型所占字節計算
對齊填充:
復合對象:對象持有對象
reference引用: 8字節,在棧內存
固定的是:對象頭16字節,一個對象最少16字節;
符合對象,32字節
http://yueyemaitian.iteye.com/blog/2033046
http://www.cnblogs.com/zhanjindong/p/3757767.html
實例數據分配策略
HotSpot虛擬機默認的分配策略為:
Longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers);相同寬度的字段總是被分配到一起。
在滿足這個前提條件的情況下,在父類中定義的變量會出現在子類之前。
如何訪問堆中對象?
必須通過棧區的reference索引,找到堆中的對象。
JVM虛擬機規範定義reference的作用,reference是指向對象的一個引用,但是引用如何實現卻未說明!取決於不同的虛擬機實現!
句柄訪問對象
通過指針訪問對象
Java語法對應的內存位置
對象-堆內存
基本類型變量-線程棧內存
基本類型變量值-線程棧內存
引用類型變量-線程棧內存
引用類型變量值-堆內存 包括數組,對象
局部變量-線程棧內存 Thread local
方法參數-線程棧內存 Thread local
對象分配規則
1.對象優先分配在Eden區
如果Eden區沒有足夠的空間時,虛擬機執行一次Minor GC。
2.大對象直接進入老年代
大對象包括:
大數組,如 private static final int _1MB = 1024 * 1024;
byte[] allocation1= new byte[2 * _1MB];
長字符串,如java io裏接收一行很長的line,大對象,並且生命周期很短,但是卻在Old代了!占用old內存
(大對象是指需要大量連續內存空間的對象)。這樣做的目的是避免在Eden區和兩個Survivor區之間發生大量的內存拷貝。通過參數-XX:PretenureSizeThreshold=3145728KB控制,超過3145728KB的對象直接進入Old
3.長期存活的對象進入老年代。
虛擬機為每個對象定義了一個年齡計數器,對象每熬過了1次Minor GC對象的年齡加1,達到閥值對象進入老年區。通過參數-XX:MaxTenuringThreshold=15(默認)控制。
4.動態判斷對象的年齡。
如果Survivor區中相同年齡的所有對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象可以直接進入老年代,無需等到MaxTenuringThreshold要求的年齡數。
5.空間分配擔保。
每次進行Minor GC時,JVM會檢查老年代剩余最大連續空間是否大於新生代所有對象總和
如果大於,則可以Minor GC
否則,檢查是否設置HandlePromotionFailure參數,是否允許擔保,
如(-XX:+HandlePromotionFailure)
如果設置允許擔保,則檢查老年代最大連續空間是否大於歷次晉升到老年代對象的平均大小,如果大於,則嘗試一次Minor GC,失敗也要Full GC
如果小於,則直接Full GC
如果不允許擔保,則直接Full GC;
5的擔保是為了4的情況準備的!
四、對象如何創建,布局?如何訪問數據