java中的對象在內存中,到底是怎樣一種存在?java
這篇隨筆,咱們就來一探究竟。可能不夠深刻,可是咱們把理解到位,深刻到咱們須要的程度這樣便可。c++
先來看下jvm的內存模型:git
程序計數器github
虛擬機棧和本地方法棧jvm
堆ide
方法區(運行時常量池)oop
(圖片出處:https://howtodoinjava.com/java/garbage-collection/jvm-memory-model-structure-and-components/)lua
這就是整個的jvm的內存模型的組成部分,那麼對象是存儲在Heap Area中的,也就是堆空間。spa
知道了存儲的地方,那具體到底是怎麼存儲的呢?咱們繼續。指針
注:全篇所說的jvm都是特指HotSpot JVM。
(剛在看OOP_CLASS)的過程當中,詳細看了一篇博客寫的很清晰,把相關的內容寫的很清晰。看了下介紹是個15年畢業的兄弟,如今已是阿里的技術專家了,再次印證的那個道理,人必須有個勤字,一勤天下無難事,還有就是恆字,檢討我本身的技術生涯,若是能作到這兩個字也早起來了。痛定思痛繼續努力。
以前其實翻過HotSpot的Klass部分的源碼,但當時並無深入的理解,今天再來引用一下。
先看整個的結構:
Java對象分爲兩個部分在jvm中,一個是對象自己,一個是對象對應的類,在jvm的代碼oopsHierarchy.hpp中能夠看到,表明對象的是下邊這些:
#ifndef CHECK_UNHANDLED_OOPS typedef class oopDesc* oop; typedef class instanceOopDesc* instanceOop; typedef class methodOopDesc* methodOop; typedef class constMethodOopDesc* constMethodOop; typedef class methodDataOopDesc* methodDataOop; typedef class arrayOopDesc* arrayOop; typedef class objArrayOopDesc* objArrayOop; typedef class typeArrayOopDesc* typeArrayOop; typedef class constantPoolOopDesc* constantPoolOop; typedef class constantPoolCacheOopDesc* constantPoolCacheOop; typedef class klassOopDesc* klassOop; typedef class markOopDesc* markOop; typedef class compiledICHolderOopDesc* compiledICHolderOop; #else
表明類的是下邊這些:註釋上也寫了,klass的結構和oop的結構是分開的
// The klass hierarchy is separate from the oop hierarchy. class Klass; class instanceKlass; class instanceMirrorKlass; class instanceRefKlass; class methodKlass; class constMethodKlass; class methodDataKlass; class klassKlass; class instanceKlassKlass; class arrayKlassKlass; class objArrayKlassKlass; class typeArrayKlassKlass; class arrayKlass; class objArrayKlass; class typeArrayKlass; class constantPoolKlass; class constantPoolCacheKlass; class compiledICHolderKlass; #endif // SHARE_VM_OOPS_OOPSHIERARCHY_HPP
爲何要分開,聽說是爲了不像c++那樣實現多態的時候須要每一個對象維護一個虛方法表,而分開的。這樣oopDesc裏咱們能夠看到,對象的定義裏,對象頭除了MarkWord以外,就是類型指針,而類型指針其實就是方法區的instanceKlass。表示的就是類在jvm裏的實例信息。
class oopDesc { friend class VMStructs; private: volatile markOop _mark; // 1 MarkWord union _metadata { // 2 類型指針 wideKlassOop _klass; narrowOop _compressed_klass; } _metadata;
// ...省略private: // field addresses in oop void* field_base(int offset) const; jbyte* byte_field_addr(int offset) const; jchar* char_field_addr(int offset) const; jboolean* bool_field_addr(int offset) const; jint* int_field_addr(int offset) const; jshort* short_field_addr(int offset) const; jlong* long_field_addr(int offset) const; jfloat* float_field_addr(int offset) const; jdouble* double_field_addr(int offset) const; address* address_field_addr(int offset) const;
這個就是OopDesc.hpp的代碼,而咱們要的InstanceOop是這樣的
#include "oops/oop.hpp" // An instanceOop is an instance of a Java Class // Evaluating "new HashTable()" will create an instanceOop. class instanceOopDesc : public oopDesc { // 省略 }; #endif // SHARE_VM_OOPS_INSTANCEOOP_HPP
明顯看到instanceOopDesc就是繼承自oopDesc的。因此咱們來分析下oopDesc的結構。
咱們知道Java對象的組成部分是:對象頭(Header)/ 實例數據(Instance Data)/ 對齊填充(Padding) 三個部分。而對象頭又包括兩部分:MarkWord 和 類型指針。
代碼中明顯能夠看到對象頭的部分,咱們已經標註出來,而實例數據就在下邊的各類field裏。
看到了OopDesc表示的對象,咱們再來看下Klass表示的類吧。