小小面試一下
前言蜜語
最近馬師傅火的不要不要的,雖然沒有搶到耗子尾汁的商標註冊權,但是必須得蹭一波馬師傅的熱度,下面就是閃電五連鞭的教學環節,你準備好了嗎!
在正式內容開始前先甩兩篇關於類加載機制和內存布局的文章,因為今天的內容多少與這兩篇文章有直接的聯繫,對這方面還比較薄弱的朋友可以先看看,地址我放在下面。
jvm┃java內存區域,跳槽大廠必會知識點!
moon不講武德!!!一個類加載機制給面試官說蒙了!!
今天本文的內容就針對剛剛模擬面試兩個問題
1.對象的創建過程
2.對象的內存布局
對象的創建過程
以下內容基於HotSpot VM 分代模型
這張圖其實就能完整的說明一個對象的創建過程到底發生了什麼,很多朋友可能一下看不懂,那麼我們就跟著左上角的一步一步來:
一個對象new出來先判斷線程棧是否能分配下如果能分配下,直接分配在棧中。如果分配不下則進行第二步。判斷該對象是否足夠大如果足夠大,則直接進入老年代。如果不夠大,則進行第三步。判斷創建對象的線程的TLAB(本地線程緩衝區)空間是否足夠如果足夠,直接分配在TLAB中。如果不夠,則進入Eden區中其他空間。然後進行第四步。GC清除如果清除掉了該對象,則直接結束。如果沒有清除掉對象,進行第5步。此刻對象進入Survivor 1 區,判斷年齡是否足夠大如果年齡足夠大,則直接進入old區域。如果年齡不夠大,則進入Survivor 2 區,然後進入第4步,循環往復。 通過這張流程圖和步驟解析大家應該對一個對象的創建過程有一個很清晰的概念了,但是其中還是有很多小細節會被忽略,為什麼jvm會在對象的創建過程中大作文章,會分這麼多種情況?為了讓大家更深入的能夠理解它,我們就再來看看下面這幾個問題:
為什麼對象會選擇先分配在棧中? 首先棧是線程私有的,將對象優先分配在棧中,可以通過pop直接將對象的所有信息,空間直接清除,當線程消亡的時候也可以直接清理這一塊兒TLAB區域。
為什麼jvm會讓大對象會直接進入老年代? 大對象需要連續的空間來存儲,如果不存入老年代對jvm說就可能是一個負擔,如果沒有足夠的空間就有可能導致提前觸發gc來清理空間來安置大對象。
為什麼會選擇先進入TLAB? TLAB是線程本地緩衝區,TLAB的好處就是防止不同線程創建對象選擇同一塊兒內存區域而產生競爭,會使其概率大大減少。
為什麼會有兩個Survivor區?並且存活且年齡不夠大的對象會從一個Survivor區轉到另一個Survivor區? 根據根可達算法,jvm會從開始尋找到所有正在使用的對象,沒有使用的就是垃圾,通常情況下,很多對象都是用完就拋棄的,所以真正在Survivor區長時間存活的對象非常少,將這部分對象從一個Survivor區轉到另一個Survivor區後,就可以直接對這個Survivor區進行全量的空間回收了,效率會很高。
對象的內存布局
作者可不是標題黨,哈哈,所以我們回到文章的標題,Object o = new Object();到底佔用多少個字節?這道題的目的其實就是考驗看你對對象的內存布局了解的是否清晰,先上圖:
在java中對象的內存布局分為兩種情況,非數組對象和數組對象,數組對象和非數組對象的區別就是需要額外的空間存儲數組的長度length。
對象頭
對象頭又分為MarkWord和Class Pointer兩部分。
MarkWord:包含一系列的標記位,比如輕量級鎖的標記位,偏向鎖標記位,gc記錄信息等等,在32位系統佔4位元組,在64位系統中佔8位元組。ClassPointer:用來指向對象對應的Class對象(其對應的元數據對象)的內存地址。在32位系統佔4位元組,在64位系統中佔8位元組。Length:只在數組對象中存在,用來記錄數組的長度,佔用4位元組Interface data
Interface data:對象實際數據,對象實際數據包括了對象的所有成員變量,其大小由各個成員變量的大小決定。(這裡不包括靜態成員變量,因為其是在方法區維護的)Padding
Padding:Java對象佔用空間是8位元組對齊的,即所有Java對象佔用bytes數必須是8的倍數,是因為當我們從磁碟中取一個數據時,不會說我想取一個字節就是一個字節,都是按照一塊兒一塊兒來取的,這一塊大小是8個字節,所以為了完整,padding的作用就是補充字節,保證對象是8位元組的整數倍。 moon在上文特意標註了32位系統和64位系統不同區域佔用空間大小的區別,這是因為對象指針在64位JVM下的尋址更長,所以想比32位會多出來更多佔用空間。
但是現在假設一個場景,公司現在項目部署的機器是32位的,你們老闆要讓你將項目遷移到64位的系統上,但是又因為64位系統比32位系統要多出更多佔用空間,怎麼辦,因為正常來說我們是不需要這一部分多餘空間的,所以jvm已經幫你考慮好了,那就是指針壓縮。
指針壓縮
-XX:+UseCompressedOops 這個參數就是JVM提供給你的解決方案,可以壓縮指針,將佔用的空間壓縮為原來的一半,起到節約空間的作用,classpointer參數大小就受到其影響。
Object o = new Object()到底佔用多少個字節?
通過剛才內存布局的學習後,這個問題就很好回答了,面試官其實就是想問你對象的內存布局是怎樣的,我們這裡就針對這個問題的結果分析下,這裡分兩種情況:
在開啟指針壓縮的情況下,markword佔用4位元組,classpoint佔用8位元組,Interface data無數據,總共是12位元組,由於對象需要為8的整數倍,Padding會補充4個字節,總共佔用16位元組的存儲空間。在沒有指針的情況下,markword佔用8位元組,classpoint佔用8位元組,Interface data無數據,總共是16位元組。結語
今天的文章和大家介紹了一個對象的創建過程,從它的出生到死亡,都經歷了什麼?也和大家詳細的說明了對象的內存布局,深入解剖了一下對象的身體構造,這下面試官再問你,可就有的聊了,這篇文章的內容還是比較肝的,需要大家認真閱讀一下,當然,也可以私下聯繫我,我是moon,下期見~