☕【JVM 技術之旅】深入挖掘Java對象的內存結構

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"📕 每日一句","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"善於利用時間的人,總會擁有充分的時間","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/17/17ee974c64c969afe24dfc53facafe29.gif","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"📕 基本概念","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在JVM虛擬機種Java對象的內存結構如圖所示分爲三大塊:對象頭(Object Header)、實例數據(Instance Data)、對齊填充(Padding)。 對象頭:標記字段、類型指針、數組長度(限於數組對象)","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7f/7facd759cdc0c7c51f283c131688939f.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象頭中主要部分相關的數據大小","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2e/2e38376c76b542e1b689d4fb3bbe8121.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象頭(Object header)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Mark Word:對象的Mark Word部分佔4個字節,其內容是一系列的標記位,比如輕量級的標記位(00),偏向鎖標記位(01)等等","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Class對象指針:Class對象指針的大小也是4個字節,其指向的位置是對象對應的Class對象(其對應的元數據對象)的內存地址。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象實際數據:這裏麪包括了對象的所有成員變量,其大小由各個成員變量的大小決定,比如:byte和boolean是1個字節,short和char是2個字節,int和float是4個字節,long和double是8個字節,refrence是4個字節","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對齊填充:最後一部分是對齊填充的字節,按8個字節填充","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"📕 對象頭(Object Header)","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/eb/ebc9af8c06e1c1b6bb0783e48dbf6515.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📕 Mark Word(標記字段)","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC 分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程 ID等。Mark Word被設計成一個非固定的數據結構以便在極小的空間內存儲儘量多的信息,它會根據自己的狀態複用自己的存儲空間","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/31/319e372b9d669dadff33a7910382fad7.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📕Klass Pointer(類型指針)","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象指向它的類數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例;","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"📕 Length(長度)","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"如果對象是一個Java數組","attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"那在對象頭中還必須有一塊用於記錄數組長度的數據","attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"因爲虛擬機可以通過普通 Java 對象的元數據信息確定 Java 對象的大小,但是從數組的元數據中無法確定數組的大小","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在32 位JVM中,存放 Class 指針的空間大小是 4 字節,Mark Word 空間大小也是4字節,因此頭部就是 8 字節,如果是數組就需要再加 4 字節表示數組的長度,如下表所示","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d4/d48d4c9d2bf8f2014f2311375be724c8.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在 64 位 JVM 中,開啓指針壓縮,頭部存放 Class 指針的空間大小還是4字節,而 Mark Word 區域會變大,變成 8 字節,也就是頭部最少爲 12 字節,如下圖所示","attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7a/7a4f532fc437dfac2065bfdaed27362d.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":false,"pastePass":false}}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"📕 實例數據(Instance Data)","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實例數據中存放了對象的字段信息。無論是從父類繼承的,還是在子類中定義的,都保存在實例數據中。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"按照一定順序存放,在滿足這個順序的條件下,父類定義的字段又會出現在子類定義的變量之前","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注意:這部分數據的存儲順序會受到虛擬機分配參數(FieldAllocationStyle)和字段在Java源碼中定義順序的影響","attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HotSpot虛擬機默認的分配策略如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"longs/doubles、ints、shorts/chars、bytes/booleans、oop(Ordinary Object Pointers)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從分配策略中可以看出,相同寬度的字段總是被分配到一起","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在滿足這個前提的條件下,父類中定義的變量會出現在子類之前","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CompactFields = true;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"如果 CompactFields參數值爲true(默認爲true),那子類之中較窄的變量也可能會插入到父類變量的空隙之中","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對齊填充:對齊填充不是必然存在的,沒有特別的含義,它僅起到佔位符的作用。由於 HotSpot VM 的自動內存管理系統要求對象起始地址必須是 8 字節的整數倍,也就是說對象的大小必須是 8 字節的整數倍。對象頭部分是 8 字節的倍數,所以當對象實例數據部分沒有對齊時,就需要通過對齊填充來補全","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e1/e12d5bd8ce016600ce5d74e2d7ddb8c3.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"📕 實際案例分析","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注意:圖中的markdown 寫錯了 應該是 markword ,請見諒!。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"在HotspotJVM中,32位機器下,Integer對象的大小是int的幾倍","attrs":{}},{"type":"text","text":"?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們都知道在java語言規範已經規定了int的大小是4個字節,那麼Integer對象的大小是多少呢?要知道一個對象的大小,那麼必須需要知道對象在虛擬機中的結構是怎樣的,來看看Hotsopt中對象在內存中的結構:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b1/b1727ad19fedf638b4dc37d824b2633f.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面這張圖裏面可以看出,對象在內存中的機構主要包含以下幾個部分:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據上面的圖,那麼我們可以得出Integer的對象的結構如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b6/b6e6d54a689b408486bc163348b24b9a.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Integer只有一個int類型的成員變量value,所以其對象實際數據部分的大小是4個字節,然後再在後面填充4個字節達到8字節的對齊,所以可以得出Integer對象的大小是16個字節。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"因此,我們可以得出Integer對象的大小是原生的int類型的4倍","attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於對象的內存結構,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"需要注意數組的內存結構和普通對象的內存結構稍微不同,因爲數據有一個長度length字段","attrs":{}},{"type":"text","text":"。所以在對象頭後面還多了一個int類型的length字段,佔4個字節,接下來纔是數組中的數據。如下圖:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0e/0e52140105c95426f942e3c7698ccee9.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"圖片總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內存對象結構","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/df/df01b2d00e8ca2d2e189d8a4eec047ed.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"border"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對象訪問方式有兩種","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/82/82986a832e9c611a0840b4d8eb73cd1c.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章