从Java类到对象的创建过程都做了些啥?内存中的对象是啥样的?
轉載自? ?從Java類到對象的創建過程都做了些啥?內存中的對象是啥樣的?
先回顧一下Java程序執行的過程:
Java程序執行時,第一步系統創建虛擬機進程,然后虛擬器用類加載器Class Loader加載java程序類文件到方法區。
方法區放哪些東西?
存放加載過的類信息、常量、靜態變量、及jit編譯后的代碼(類方法)等數據的內存區域。它是線程共享的。
方法區存放的信息包括:類的基本信息、運行時常量池、變量字段信息、方法信息等。這部分的詳細介紹看下面鏈接的文章。
詳細Java程序運行的內存結構介紹 點此處
簡要過程:
類加載完成后,主線程運行static main()時在虛擬機棧中建棧幀,壓棧。
執行到new Object()時,在堆heap里創建對象。
對象創建的過程就是堆上分配實例對象內容空間的過程,在堆中對象內存空間的具體結構如下:
對象頭?這個頭包括兩個部分,第一部分用于存儲自身運行時的數據例如GC標志位、哈希碼、鎖狀態等信息。第二部分存放指向方法區類靜態數據的指針。
實例變量?存放類的屬性數據信息,包括父類的屬性信息。如果是數組的實例部分還包括數組的長度。這部分內存按4字節對齊。
填充數據?這是因為虛擬機要求對象起始地址必須是8字節的整數倍。填充數據不是必須存在的,僅僅是為了字節對齊。HotSpot VM的自動內存管理要求對象起始地址必須是8字節的整數倍。對象頭本身是8的倍數,當對象的實例變量數據不是8的倍數,便需要填充數據來保證8字節的對齊。另外,堆上對象內存的分配是并發進行的.
然后執行類的構造函數初始化。
Java虛擬機規范規定該區域可拋出OutOfMemoryError。
詳細步驟
例如:
Dog?dog =?new?Dog();
當虛擬機執行到new指令時,它先在常量池中查找“Dog”,看能否定位到Dog類的符號引用;如果能,說明這個類已經被加載到方法區了,則繼續執行。如果沒有,就讓Class Loader先執行類的加載。
然后,虛擬機開始為該對象分配內存,對象所需要的內存大小在類加載完成后就已經確定了。這時候只要在堆中按需求分配空間即可。具體分配內存時有兩種方式,第一種,內存絕對規整,那么只要在被占用內存和空閑內存間放置指針即可,每次分配空間時只要把指針向空閑內存空間移動相應距離即可,當某對象被GC回收后,則需要進行某些對象內存的遷移。第二種,空閑內存和非空閑內存夾雜在一起,那么就需要用一個列表來記錄堆內存的使用情況,然后按需分配內存。
對于多線程的情況,如何確保一個線程分配了對象內存但尚未修改內存管理指針時,其他線程又分配該塊內存而覆蓋的情況?有一種方法,就是讓每一個線程在堆中先預分配一小塊內存(TLAB本地線程分配緩沖),每個線程只在自己的內存中分配內存。但對象本身按其訪問屬性是可以線程共享訪問的。
內存分配到后,虛擬機將分配的內存空間都初始化為零值(不包括對象頭)。實例變量按變量類型初始化相應的默認值(數值型為0,boolan為false),所以實例變量不賦初值也能使用。接著設置對象頭信息,比如對象的哈希值,GC分代年齡等。
從虛擬機角度,此時一個新的對象已經創建完成了。但從我們程序運行的角度,新建對象才剛剛開始,對象的構造方法還沒有執行。只有執行完構造方法,按構造方法進行初始化后,對象才是徹底創建完成了。
構造函數的執行還涉及到調用父類構造器,如果沒有顯式聲明調用父類構造器,則自動添加默認構造器。
到此,new運算符可以返回堆中這個對象的引用了。
此刻,會根據dog這個變量是實例變量、局部變量或靜態變量的不同將引用放在不同的地方:
如果dog局部變量,dog變量在棧幀的局部變量表,這個對象的引用就放在棧幀。
如果dog是實例變量,dog變量在堆中,對象的引用就放在堆。
如果dog是靜態變量,dog變量在方法區,對象的引用就放在方法區。
總結
以上是生活随笔為你收集整理的从Java类到对象的创建过程都做了些啥?内存中的对象是啥样的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 类和对象运行时在内存里是怎么样的?各种变
- 下一篇: 关于Heap Dump