1. 程式人生 > 實用技巧 >最短路演算法dijkstra演算法

最短路演算法dijkstra演算法

接著上一篇,我們繼續來講oopDesc相關的子類。

3、instanceOopDesc類

instanceOopDesc類的例項表示除陣列物件外的其它物件。在HotSpot中,物件在記憶體中儲存的佈局可以分為三塊區域:物件頭(header)、物件欄位資料(field data)和對齊填充(padding),如下圖所示。

下面詳細介紹一下這3個組成部分。

1.物件頭

可以看到物件頭分為兩個部分,一個就是“Mark Word”,另外還有儲存指向方法區物件型別資料的指標_klass或_compressed_klass。這兩個都在介紹oopDesc類時詳細介紹過,這裡不再介紹。

2物件欄位資料

物件欄位資料儲存Java原始碼中定義的各種型別欄位內容,具體包括父類繼承及子類定義的物件欄位。

儲存順序受到HotSpot分配策略引數(FieldAllocationStyle)和欄位在Java原始碼中定義順序的影響。預設分配策略為:long/double、int、short/char、boolean、oops(物件指標,32位系統佔用4位元組,64位系統佔用8位元組),可以看到,相同寬度的欄位總被分配到一起。

如果虛擬機器器的-XX:+CompactFields引數為true,子類中較窄的變數可能插入到父類變數空隙中,以壓縮節省空間。例如,當碰到long/doubles時,會將一些短型別插入long/doubles和header的空隙中。(空隙:64位系統開啟壓縮指標,header佔12個位元組,剩下的4個位元組就是空隙。更多欄位儲存順序的內容將在第XX章詳細介紹。

3對齊填充部分

對齊填充部分不是必須的,只起佔位符作用,沒有其他含義。HotSpot虛擬機器器要求物件大小必須是8位元組的整數倍,物件頭是8位元組整數倍,所以填充是對例項資料沒有對齊的情況來說的。

在建立instanceOop物件時會呼叫allocate_instance()方法,這個方法的實現如下:

instanceOop InstanceKlass::allocate_instance(TRAPS) {

  int size = size_helper();  // Query before forming handle.

  KlassHandle h_k(THREAD, this);

  instanceOop i;
i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
return i;
}

呼叫instanceKlass中的size_helper()方法獲取建立instanceOop物件所需要的記憶體大小,呼叫CollectedHeap::obj_allocate()方法分配size大小的記憶體。首先介紹size_helper()方法的實現,如下:

// Use this to return the size of an instance in heap words:
int size_helper() const {
return layout_helper_to_size_helper(layout_helper());
} int layout_helper() const {
return _layout_helper;
} static int layout_helper_to_size_helper(jint lh) {
assert(lh > (jint)_lh_neutral_value, "must be instance");
// Note that the following expression discards _lh_instance_slow_path_bit.
return lh >> LogHeapWordSize;
}

從_layout_helper屬性中獲取大小,之前介紹過這個綜合描述符,如果為InstanceKlass,則組合數字中含有的是instanceOop物件的大小,在設定時呼叫的是instance_layout_helper()方法,如下:

static jint instance_layout_helper(jint size, bool slow_path_flag) {
return (size << LogHeapWordSize) // LogHeapWordSize=3
| (slow_path_flag ? _lh_instance_slow_path_bit : 0); // 例項慢速分配有關
}

獲取size時需要向右移動3位即可。這個方法在建立InstanceKlass物件時會呼叫,不過size通常會初始化為0,在呼叫parseClassFile()方法計算完例項的大小時,還會呼叫此方法更新為真正需要的instanceOop物件大小,在解析類檔案時會詳細介紹例項大小的計算過程。 

在InstanceKlass::allocate_instance()方法中呼叫CollectedHeap::obj_allocate()方法分配size大小的記憶體並將記憶體初始化為零值,方法將會在介紹垃圾回收時詳細介紹,這裡不介紹。

4、arrayOopDesc類

arrayOopDesc類的例項表示Java陣列物件。具體的基本型別陣列(物件)或物件型別陣列(物件)由具體的C++中定義的子類例項來表示。在HotSpot虛擬機器器中,陣列物件在記憶體中儲存的佈局可以分為三塊區域:物件頭(header)、物件欄位資料(field data)和對齊填充(padding),如下圖所示。

與Java物件記憶體佈局唯一不同之處在於,陣列物件的物件頭中還會儲存陣列的長度length,佔用的記憶體空間為4位元組。在64位系統下,存放_metadata的空間大小是8位元組,_mark是8位元組,length是4位元組,物件頭為20位元組,由於要按8位元組對齊,所以會填充4位元組,最終佔用24位元組。64位開啟指標壓縮的情況下,存放_metadata的空間大小是4位元組,_mark是8位元組,length是4位元組,物件頭為16位元組。

5、arrayOopDesc類的子類

typeArrayOopDesc類的例項表示Java基本型別陣列(物件),objArrayOopDesc類的例項表示Java物件型別陣列(物件)。當需要建立typeArrayOopDesc物件時,通常會呼叫oopFactory類中定義的工廠方法,例如建立一個boolean陣列,則呼叫new_boolArray()方法,如下:

static typeArrayOop    new_boolArray(int length, TRAPS) {
TypeArrayKlass* tak = TypeArrayKlass::cast(Universe::boolArrayKlassObj());
return tak->allocate(length, CHECK_NULL);
}

呼叫Universe::boolArrayKlassObj()方法獲取_charArrayKlassObj屬性的值,也就是之前介紹的、呼叫TypeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK)方法建立的表示boolean陣列的TypeArrayKlass物件,然後呼叫TypeArrayKlass中的allocate()方法建立typeArrayOop物件,如下:

typeArrayOop allocate(int length, TRAPS) {
return allocate_common(length, true, THREAD);
} typeArrayOop TypeArrayKlass::allocate_common(int length, bool do_zero, TRAPS) {
assert(log2_element_size() >= 0, "bad scale");
if (length >= 0) {
if (length <= max_length()) {
size_t size = typeArrayOopDesc::object_size(layout_helper(), length);
KlassHandle h_k(THREAD, this);
typeArrayOop t;
CollectedHeap* ch = Universe::heap();
if (do_zero) {
t = (typeArrayOop)CollectedHeap::array_allocate(h_k, (int)size, length, CHECK_NULL);
} else {
t = (typeArrayOop)CollectedHeap::array_allocate_nozero(h_k, (int)size, length, CHECK_NULL);
}
return t;
}
}
}

引數length表示建立陣列的大小,而do_zero表示是否需要在分配陣列記憶體時,將記憶體初始化為堆值。方法首先呼叫typeArrayOopDesc::object_size()方法從_layout_helper中獲取陣列的大小,方法的實現如下:

static int object_size(int lh, int length) {
int instance_header_size = Klass::layout_helper_header_size(lh);
int element_shift = Klass::layout_helper_log2_element_size(lh); julong size_in_bytes = length;
size_in_bytes <<= element_shift;
size_in_bytes += instance_header_size;
// 按8位元組對齊,填充的一部分
julong size_in_words = ((size_in_bytes + (HeapWordSize-1)) >> LogHeapWordSize); return align_object_size((intptr_t)size_in_words); // 對齊,填充的一部分
}

之前介紹過,當為ArrayKlass時,_layout_helper屬性是個組合數字,呼叫Klass::layout_helper_header_size()方法獲取陣列頭元素的位元組數;呼叫Klass::layout_helper_log2_element_size()方法獲取陣列元素的大小,對於陣列元素是boolean型別來說,這個值為1。

size = instance_header_size + length<<element_shift + 對齊填充

也就是物件頭加上例項資料,然後再加上對齊填充。  

在TypeArrayKlass::allocate_common()方法中獲取到TypeArrayOopDesc物件所需要分配的記憶體大小後,就會呼叫CollectedHeap::array_allocate()或CollectedHeap::array_allocate_nozero()方法在堆上分配記憶體空間,關於在堆上分配記憶體的方法在後面介紹垃圾回收時會詳細介紹,這裡不介紹。

objArrayOop的建立與typeArrayOop的建立非常類似,也是呼叫oopFactory類中的工廠方法new_objectArray()方法,然後呼叫ObjArrayKlass::allocate()方法,這裡不在介紹。

相關文章的連結如下:

1、在Ubuntu 16.04上編譯OpenJDK8的原始碼

2、除錯HotSpot原始碼

3、HotSpot專案結構 

4、HotSpot的啟動過程

5、HotSpot二分模型(1)

6、HotSpot的類模型(2)

7、HotSpot的類模型(3)

8、HotSpot的類模型(4)

9、HotSpot的物件模型(5)

關注公眾號,有HotSpot原始碼剖析系列文章!