前言
最近看到了這樣的一篇文章, 一個對象的引用佔多少個字節呢?4個?8個?算出來都不對 , 呵呵 這是一個 之前想要弄明白, 但是這塊的代碼 似乎是看着有點複雜, 所以 一直沒有花時間來整理一下, 呵呵 最近看到了一篇文章, 看了一下 R大 的分析
然後 自己結合自己的 實際情況, 整理了一一下 一些東西
以下代碼, 截圖 基於 jdk9
首先是測試用例
package com.hx.test04;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* TypeSizeOf
*
* @author Jerry.X.He <[email protected]>
* @version 1.0
* @date 2020-03-07 16:04
*/
public class Test05TypeSizeOf {
// fields
private long id = 1;
private Test05TypeSizeOf test2 = null;
private List<Test05TypeSizeOf> list = new ArrayList<>();
private Date date = new Date();
private byte status = 2;
private byte count = 3;
// refer : https://hllvm-group.iteye.com/group/topic/38670
// -ea -javaagent:/Users/jerry/Tmp/agent/HelloWorld-1.0-SNAPSHOT_agent.jar
// vm 調試相關參數爲 -da -dsa -Xint -Xmx100M -XX:+UseSerialGC -javaagent:/Users/jerry/Tmp/agent/HelloWorld-1.0-SNAPSHOT_agent.jar com.hx.test04.Test05TypeSizeOf
public static void main(String[] args) {
long sizeOfTest05TypeOfSizeOf = Test01PremainAgentClazz.inst.getObjectSize(new Test05TypeSizeOf());
System.out.println(sizeOfTest05TypeOfSizeOf);
}
}
測試結果如下
第一個輸出是 javaagent 裏面的輸出, 第二個輸出是 這裏 main 方法裏面的輸出
那麼, 這裏輸出的結果是 40, 好了 我們現在知道了 Test05TypeSizeOf 會佔用 40個 字節, 那麼 他又是如何計算的呢, 在內存中是如何排列的呢 ?
佈局的計算方式?
classFileParser.layout_fields 相關代碼片段如下
int nonstatic_oop_space_count = 0;
int nonstatic_word_space_count = 0;
int nonstatic_short_space_count = 0;
int nonstatic_byte_space_count = 0;
int nonstatic_oop_space_offset = 0;
int nonstatic_word_space_offset = 0;
int nonstatic_short_space_offset = 0;
int nonstatic_byte_space_offset = 0;
// Try to squeeze some of the fields into the gaps due to
// long/double alignment.
if (nonstatic_double_count > 0) {
int offset = next_nonstatic_double_offset;
next_nonstatic_double_offset = align_size_up(offset, BytesPerLong);
if (compact_fields && offset != next_nonstatic_double_offset) {
// Allocate available fields into the gap before double field.
int length = next_nonstatic_double_offset - offset;
assert(length == BytesPerInt, "");
nonstatic_word_space_offset = offset;
if (nonstatic_word_count > 0) {
nonstatic_word_count -= 1;
nonstatic_word_space_count = 1; // Only one will fit
length -= BytesPerInt;
offset += BytesPerInt;
}
nonstatic_short_space_offset = offset;
while (length >= BytesPerShort && nonstatic_short_count > 0) {
nonstatic_short_count -= 1;
nonstatic_short_space_count += 1;
length -= BytesPerShort;
offset += BytesPerShort;
}
nonstatic_byte_space_offset = offset;
while (length > 0 && nonstatic_byte_count > 0) {
nonstatic_byte_count -= 1;
nonstatic_byte_space_count += 1;
length -= 1;
}
// Allocate oop field in the gap if there are no other fields for that.
nonstatic_oop_space_offset = offset;
if (length >= heapOopSize && nonstatic_oop_count > 0 &&
allocation_style != 0) { // when oop fields not first
nonstatic_oop_count -= 1;
nonstatic_oop_space_count = 1; // Only one will fit
length -= heapOopSize;
offset += heapOopSize;
}
}
}
int next_nonstatic_word_offset = next_nonstatic_double_offset +
(nonstatic_double_count * BytesPerLong);
int next_nonstatic_short_offset = next_nonstatic_word_offset +
(nonstatic_word_count * BytesPerInt);
int next_nonstatic_byte_offset = next_nonstatic_short_offset +
(nonstatic_short_count * BytesPerShort);
int next_nonstatic_padded_offset = next_nonstatic_byte_offset +
nonstatic_byte_count;
// let oops jump before padding with this allocation style
if( allocation_style == 1 ) {
next_nonstatic_oop_offset = next_nonstatic_padded_offset;
if( nonstatic_oop_count > 0 ) {
next_nonstatic_oop_offset = align_size_up(next_nonstatic_oop_offset, heapOopSize);
}
next_nonstatic_padded_offset = next_nonstatic_oop_offset + (nonstatic_oop_count * heapOopSize);
}
classFileParser.layout_fields 這裏是佈局的處理, 默認情況下 allocation_style = 1
將數據分爲了兩批, 一個批次是 *_space_count[Word, Short, Byte, Oop], 是存放在 align_size_up(markOop + klass, BytesPerLong) 的空隙
另外的一部分 : longs/doubles, ints, shorts/chars, bytes, oops, padded fields, 排列
對於我們這裏的場景, 如下
對應於我們這裏的 Test05TypeSizeOf 的實際情況, 佈局大致如下
markOop[8] + klass[4]
byte status[1]
byte count[1]
padding[2]
long [8]
test2 [4]
list [4]
date [4]
padding [4]
合計 40 bytes, 5 word
運行時的數據?
那麼我們理論上得到了數據的排列如下, 那麼我們看一下 實際的運行時的一些情況呢 ?
HSDB attach 到目標進程, inspect Test05TypeSizeOf 的實例
inspect 0x0000000795969ab8
Oop for com/hx/test04/Test05TypeSizeOf @ 0x0000000795969ab8
_mark: 1
_metadata._compressed_klass: InstanceKlass for com/hx/test04/Test05TypeSizeOf
id: 1
test2: null
list: Oop for java/util/ArrayList @ 0x0000000795969ae0
date: Oop for java/util/Date @ 0x00000007959831f8
status: 2
count: 3
查看一下 的內存數據呢 ?
mem 0x0000000795969ab8 5
0x0000000795969ab8: 0x0000000000000001
0x0000000795969ac0: 0x00000302f800c354
0x0000000795969ac8: 0x0000000000000001
0x0000000795969ad0: 0xf2b2d35c00000000
0x0000000795969ad8: 0x00000000f2b3063f
# 數據拆解如下
0x0000000795969ab8 : 0x0000000000000001 爲 markOop
0x0000000795969ac0 : 0xf800c354 爲 compressedKlass
0x0000000795969ac4 : 0x02 爲 status
0x0000000795969ac5 : 0x03 爲 count
0x0000000795969ac6 : 0x0000 爲 padding
0x0000000795969ac8 : 0x0000000000000001 爲 id
0x0000000795969ad0 : 0x0000000 爲 test2
0x0000000795969ad4 : 0xf2b2d35c 爲 list
0x0000000795969ad8 : 0xf2b3063f 爲 date
0x0000000795969ae0 : 0x0000000 爲 padding
但是 發現一個問題, 爲什麼記錄的 數據的地址 和 給定的對象的 oop 的地址不一樣呢 ?
compressedOops 相關
從上面可以看到, 實際存儲的 oop 的地址數據 和 給定的 oop 的真實地址是不一樣的, 那麼這是怎麼回事呢?, 兩個數據又有什麼關聯呢 ?
實際存儲的 list 地址爲 0xf2b2d35c, list 的真實地址爲 0x0000000795969ae0
這是因爲一個 UseCompressedOops 特性, 那麼我們來看下 壓縮之後的地址 和 原來的地址的關係吧
oop.decode_heap_oop_not_null 相關實現如下
我們這裏, base 爲 0, shift 爲 3 (<< 3 等價於 * 8[字長])
base, shift 初始化的地方在這裏
至於 base 爲什麼是 0, shift 是 3, 我們可以暫時不深究
好了, 兩者之間的關係大概就是這樣, 那麼 apply 到這裏的實際情況呢 ?
實際存儲的 list 地址爲 0xf2b2d35c, list 的真實地址爲 0x0000000795969ae0
0xf2b2d35c * 8 = 0x795969ae0
那麼同理 另外一個 date 的地址記錄的是 0xf2b3063f, date 的真實地址爲 0x00000007959831f8
0xf2b3063f * 8 = 0x7959831f8
好了, 這裏的相關東西 大概就這些了
完
參考
一個對象的引用佔多少個字節呢?4個?8個?算出來都不對