對象創(chuàng)建流程
當JAVA虛擬機碰到new字節(jié)碼指令時,首先會去常量池中查找是否有對應的類名(也就是去查找是否有對應的符號引用),然后去檢查這個符號引用代表的類是否已經(jīng)被加載,解析和初始化過。如果沒有會先進行類加載過程。
當類加載后,虛擬機將會為其分配內(nèi)存,為其分配的內(nèi)存大小是可知的,下面的內(nèi)存布局將會講解為什么是可知的。
分配內(nèi)存
分配內(nèi)存這時候有兩種情況:
1.假如堆里面的內(nèi)存是整齊的,用過的在一邊,沒有用過的內(nèi)存放在另外一邊(后期配圖),這個時候中間有個指針來作為兩邊內(nèi)存的界限,當內(nèi)存分配時,指針移動對象內(nèi)存大小對應的距離即可,這種叫==指針碰撞==。
2.堆里面的內(nèi)存空間不是規(guī)整的,這就需要記錄下來哪些內(nèi)存是可用的,哪些內(nèi)存是已經(jīng)被占用了的。這種方式就叫做==空閑列表==:將內(nèi)存中空閑內(nèi)存塊記錄到列表里面。當分配對象時,直接從空閑列表里面進行取出對應大小的內(nèi)存塊即可。
這兩種情況又是根據(jù)采用的垃圾收集器是否帶有==空間壓縮整理的能力==劃分,如果垃圾收集器已經(jīng)具備了空間壓縮整理的能力那么他的內(nèi)存空間就是==被整理好的==,直接使用==指針碰撞==就好;但是如果是基于清除算法決定的垃圾回收器時,就只能用復雜的空閑列表來分配內(nèi)存。
并發(fā)解決方法
但是如果發(fā)生并發(fā)的話,可能在分配一個對象空間的時候又碰到另外一個線程也在分配空間,這個時候就會出現(xiàn)問題,解決方式有兩種:
1.通過CAS進行同步處理,基于失敗重試的原則;2.將堆里面的空間進行按線程分配,每個線程在隊中都會有塊內(nèi)存,當線程分配內(nèi)存時,直接分配到自己線程的那塊內(nèi)存當中,當那小塊內(nèi)存用完時,在進行CAS同步申請新的內(nèi)存,這種小塊內(nèi)存叫做==本地線程分配緩存(TLAB)==。
設置初始值
==分配完內(nèi)存之后需要給這部分內(nèi)存設置零值,不包括對象頭。當通過TLAB分配內(nèi)存時,其實在分配內(nèi)存的時候就可以設置零值,不需要等到分配完在設置,因為這部分內(nèi)存區(qū)域是已知的不會出現(xiàn)分配時產(chǎn)生并發(fā)的問題==
在程序中可能會出現(xiàn)的問題
解釋:這步操作也就是說當對象分配到內(nèi)存后就可以直接使用里面的字段,==但是這個是初始值==,如果說當我分配完內(nèi)存后直接使用這個字段的話程序肯定會出問題(因為CPU是亂序執(zhí)行的,當兩個操作互不關聯(lián)時,一個操作耗時一個操作不耗時,這時候CPU會進行優(yōu)化讓不耗時的先運行。而且一個創(chuàng)建對象的過程需要多行字節(jié)碼來完成,所以可能會出現(xiàn)重排序的問題,但是這個概率特別低)這時候就需要用volatile關鍵字來保證有序性。
?其本身是通過在JVM平臺上面的Load,Store兩個讀寫屏障組合來保證的,對應于intel的X86來說是基于MESI協(xié)議來保證的。其實JVM平臺規(guī)定了一些不能亂序執(zhí)行的原則:HappenBefore原則,里面就規(guī)定了volitaile關鍵字
?
設置對象頭
當對象中的字段設置為對應的默認值(零值)時,需要設置對象頭里面的數(shù)據(jù),這部分數(shù)據(jù)包括兩部分:
對象頭數(shù)據(jù)結(jié)構(gòu)
1.==對象自身運行時的數(shù)據(jù)== 比如:哈希碼(延遲到真正調(diào)用hashcode()方法時才生成) 鎖狀態(tài)標志 線程持有鎖 偏向鎖的線程ID 偏向時間戳 對象分代年齡 ...... 在未開啟壓縮指針的情況下,根據(jù)32位虛擬機和64位虛擬機不同,這部分數(shù)據(jù)的總大小分別是32個比特和64個比特。這部分數(shù)據(jù)叫做“Mark Word”,由于對象運行時存儲的數(shù)據(jù)很多,所以Mark Word是一個動態(tài)的數(shù)據(jù)結(jié)構(gòu),有些數(shù)據(jù)其實根本用不到所以某些數(shù)據(jù)其實是沒有必要立馬就存儲的。
?32位的虛擬機中,MarkWord是32個比特,其中哈希碼占用25個比特,分代年齡占用4個,鎖標志位占用兩個,剩下的另外一個比特固定為0。
?
2.==類型指針== 指向類的元數(shù)據(jù)信息,通過這個指針來確定該對象屬于哪個類的實例。
?(不是所有的虛擬機都必須在對象數(shù)據(jù)上設置類型指針)
?
==當對象是數(shù)組。。。。==
?如果對象是數(shù)組,在對象頭中還會記錄數(shù)組長度,普通JAVA對象可以通過找到類的元數(shù)據(jù)信息確定JAVA對象的大小,但是數(shù)組長度是不能通過類的元數(shù)據(jù)信息推導出來的,所以需要在對象頭中設置數(shù)組長度
?
Class文件的<.init>
當設置完字段的默認值和對象頭的數(shù)據(jù)后,這個時候該調(diào)用Class對象的<.init>方法了即構(gòu)造函數(shù)。
對象的內(nèi)存布局
當了解完前面的對象創(chuàng)建流程時,相信對于對象在堆中的內(nèi)存布局也已經(jīng)有兩大概的輪廓了,接下來進行總結(jié):
==分為三部分:對象頭,實例數(shù)據(jù),對齊填充==
1.對象頭前面已經(jīng)詳細講過了,就不在闡述了
2.實例數(shù)據(jù):記錄父類和當前類中定義的字段,存儲的順序默認是:long/doubles , ints , shorts/chars , bytes/booleans , oops。默認順序遵從的原則是相同寬度的字段分配到一起,接著父類定義的變量在子類定義的變量的簽名。
3.對齊填充:==不是必然的== 占位符。由于HotSpot虛擬機自動內(nèi)存管理系統(tǒng)要求對象的起始地址必須是8字節(jié)的整數(shù)倍,也就是對象的大小都必須是8的倍數(shù)。對象頭剛剛說了無非是32比特或者64比特默認就是八字節(jié)的,所以當實例數(shù)據(jù)滿足八的倍數(shù)時,就不需要占位符,這部分數(shù)據(jù)也就沒有;如果不滿足八的倍數(shù),將添加占位符使整個對象大小為八的倍數(shù)。
-
JAVA
+關注
關注
20文章
2989瀏覽量
109642 -
JVM
+關注
關注
0文章
160瀏覽量
12620 -
虛擬機
+關注
關注
1文章
966瀏覽量
29358
發(fā)布評論請先 登錄
解讀rtt的c的面向對象的對象創(chuàng)建及其啟動初始化流程
java如何創(chuàng)建對象的分析
使用JavaScript創(chuàng)建對象的方法和案例
一文詳解Java對象的內(nèi)存布局
JVM內(nèi)存布局的多方面了解
在JavaScript中動態(tài)的創(chuàng)建QML對象
探討JVM的內(nèi)存布局
Java中創(chuàng)建對象有哪些方式
Java反射技術實現(xiàn)對象的創(chuàng)建

JVM內(nèi)存大對象監(jiān)控和優(yōu)化問題描述及解決辦法

淺析JVM內(nèi)存大對象監(jiān)控和優(yōu)化實踐的過程

評論