JVM对象创建与内存分配机制学习总结
對(duì)象的創(chuàng)建過(guò)程
1、類加載檢查
虛擬機(jī)遇到一條new指令時(shí)(new關(guān)鍵詞、對(duì)象克隆、對(duì)象序列化等),首先會(huì)檢查這個(gè)類是否已被加載、解析和初始化過(guò)。如果沒(méi)有,要先執(zhí)行相應(yīng)的類加載過(guò)程。
2、分配內(nèi)存
內(nèi)存分配有兩種方法:
“指針碰撞”(Bump the Pointer)默認(rèn)用指針碰撞
在為對(duì)象開(kāi)辟內(nèi)存空間時(shí),會(huì)把內(nèi)存順序擺放,即已用內(nèi)存和空閑內(nèi)存分開(kāi)存放,通過(guò)指針向空閑空間那邊挪動(dòng)一段與對(duì)象大小相等的距離來(lái)實(shí)現(xiàn)內(nèi)存分配。
“空閑列表”(Free List)
這種方式是已使用的內(nèi)存和空 閑的內(nèi)存相互交錯(cuò),虛擬機(jī)會(huì)維護(hù)一個(gè)列表,記錄上哪些內(nèi)存塊是可用的,在分配的時(shí)候從列表中找到一塊足夠大的空間劃分給對(duì)象實(shí)例, 并更新列表上的記錄。
內(nèi)存分配時(shí)的并發(fā)問(wèn)題
分配內(nèi)存時(shí),給對(duì)象A分配時(shí)指針沒(méi)來(lái)得及修改,對(duì)象B又同時(shí)使用了原來(lái)的指針來(lái)分配內(nèi)存。
解決方案:
CAS(compare and swap)
就是多個(gè)對(duì)象同時(shí)去搶一塊內(nèi)存空間,誰(shuí)搶到了就分配給誰(shuí),沒(méi)搶到的會(huì)重試去搶下一塊內(nèi)存空間
TLAB本地線程分配緩沖(Thread Local Allocation Buffer)JDK1.8默認(rèn)使用此方式
就是每個(gè)線程在Java堆的Eden區(qū)中預(yù)先分配一小塊內(nèi)存,分配內(nèi)存時(shí)會(huì)優(yōu)先往自己線程的內(nèi)存區(qū)域去分配,可以通過(guò)-XX:+/-UseTLAB參數(shù)來(lái)設(shè)定虛擬機(jī)是否使用TLAB(JVM會(huì)默認(rèn)開(kāi)啟),-XX:TLABSize 指定TLAB大小。
3、初始化
和靜態(tài)變量初始化一樣,內(nèi)存分配完后,JVM需要將分配到的內(nèi)存空間都初始化為零值(不包括對(duì)象頭)。
4、設(shè)置對(duì)象頭
一個(gè)完整的對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為3塊區(qū)域:對(duì)象頭、 實(shí)例數(shù)據(jù)、對(duì)齊填充(保證對(duì)象時(shí)8字節(jié)的整數(shù)倍)
對(duì)象頭中包含,MarkWord,類型指針,數(shù)組長(zhǎng)度。
MarkWord:其中包括對(duì)象的hashCode,分代年齡,鎖指針等。
類型指針:對(duì)象new出來(lái)之后會(huì)存放在堆內(nèi)存中,類型指針就是指向這個(gè)對(duì)象所在的方法區(qū)中的類信息的指針(jdk1.6 update14開(kāi)始,64位操作系統(tǒng)JVM支持指針壓縮)
數(shù)組長(zhǎng)度:只有數(shù)組對(duì)象才有
指針壓縮(默認(rèn)開(kāi)啟)
啟用指針壓縮:-XX:+UseCompressedOops,禁止指針壓縮:-XX:-UseCompressedOops
-XX:+UseCompressedOops 默認(rèn)開(kāi)啟的壓縮所有指針
-XX:+UseCompressedClassPointers 默認(rèn)開(kāi)啟的壓縮對(duì)象頭里的類型指針Klass Pointer
為什么要指針壓縮?
為了節(jié)省內(nèi)存空間,在jvm中,32位地址最大支持4G內(nèi)存(2的32次方),通過(guò)一定的壓縮算法壓縮指針后,可以把35位(32G)的表述地址轉(zhuǎn)化成32位存放在堆內(nèi)存中,使用時(shí)通過(guò)cpu寄存器解壓成35位,這樣就可以讓JVM用32位地址就可以支持<=32G的內(nèi)存配置。
堆內(nèi)存小于4G時(shí),不需要啟用指針壓縮。
堆內(nèi)存大于32G時(shí),壓縮指針會(huì)失效,會(huì)強(qiáng)制使用64位尋址,所以在64位平臺(tái)中使用32位指針(實(shí)際存儲(chǔ)用64位),內(nèi)存使用會(huì)多出1.5倍左右。
5、執(zhí)行init方法
對(duì)初始化的屬性進(jìn)行賦值,并且執(zhí)行對(duì)象中的構(gòu)造方法。
對(duì)象內(nèi)存分配
一、對(duì)象逃逸分析
對(duì)象逃逸分析就是分析對(duì)象動(dòng)態(tài)作用域,就是對(duì)象在方法中被定義后,是否可能被外部方法所引用。
public void test() {User user = this.user1(); } public User user1() {User user = new User();user.setName("pingfan");return user; } public void user2() {User user = new User();user.setName("pingfan"); }1、逃逸分析:JDK7之后默認(rèn)開(kāi)啟
user1方法中new了一個(gè)User對(duì)象,最后又把User對(duì)象當(dāng)作返回值返回給調(diào)用者,這就叫對(duì)象逃逸。user2中的User對(duì)象沒(méi)有被外部引用,它就沒(méi)有逃逸,這種沒(méi)有逃逸的對(duì)象可以優(yōu)先分配到棧中,因?yàn)榉椒ńY(jié)束后這個(gè)對(duì)象就可以確定為是無(wú)效對(duì)象,讓它在方法結(jié)束時(shí)跟隨棧內(nèi)存一起被回收掉,可以減少堆內(nèi)存的gc壓力。
1、標(biāo)量替換:JDK7之后默認(rèn)開(kāi)啟
標(biāo)量就是java的八大數(shù)據(jù)類型,一個(gè)對(duì)象由最底層都是由基本數(shù)據(jù)類型組成的,所以一個(gè)java對(duì)象可以稱為聚合量。通過(guò)逃逸分析確定該對(duì)象不會(huì)被外部引用時(shí),這個(gè)對(duì)象嘗試往棧內(nèi)存中分配空間,但棧內(nèi)存沒(méi)有一塊一大塊連續(xù)空間導(dǎo)致對(duì)象內(nèi)存不夠分配,這時(shí)如果對(duì)象可以被進(jìn)一步分解時(shí),JVM會(huì)將該對(duì)象的成員變量分解多塊,分別存放在內(nèi)存碎片中。
1、逃逸分析參數(shù):
開(kāi)啟逃逸分析:-XX:+DoEscapeAnalysis
關(guān)閉逃逸分析:-XX:-DoEscapeAnalysis
開(kāi)啟標(biāo)量替換:-XX:+EliminateAllocations
二、對(duì)象分配流程
1、大對(duì)象直接進(jìn)入老年代
就是對(duì)象大小超過(guò)年輕代的內(nèi)存大小,這類對(duì)象會(huì)直接進(jìn)入老年代,大對(duì)象大小可以設(shè)置
2、長(zhǎng)期存活的對(duì)象進(jìn)入老年代
就是對(duì)象的分代年齡達(dá)到一定值后進(jìn)入老年代(默認(rèn)為15,CMS收集器默認(rèn)6,不同的垃圾收集器不同)分代年齡可以通過(guò)參數(shù)設(shè)置
3、對(duì)象動(dòng)態(tài)年齡判斷
Minor Gc后,如果需要移動(dòng)的一批對(duì)象的總大小,大于這塊Survivor區(qū)域內(nèi)存的50%,就會(huì)把年齡大于等于這批對(duì)象中年齡最大值的對(duì)象都放進(jìn)老年代。
例:在一批對(duì)象中,其中年齡1+年齡2+年齡n的多個(gè)年齡段對(duì)象的內(nèi)存總和超過(guò)了Survivor區(qū)域的50%,此時(shí)就會(huì)把年齡>=n的對(duì)象都放進(jìn)老年代
4、老年代空間分配擔(dān)保機(jī)制jdk1.8默認(rèn)設(shè)置了參數(shù)
每次Minor Gc前JVM都會(huì)計(jì)算下老年代剩余空間,如果剩余空間大于年輕代里現(xiàn)有的所有對(duì)象大小總和(包括垃圾對(duì)象)就會(huì)直接Minor Gc,如果空間不足,但是開(kāi)啟了此機(jī)制時(shí),就會(huì)看老年代的剩余空間,是否大于之前每一次Minor Gc后進(jìn)入老年代的對(duì)象平均值,大于的話就Minor Gc,小于或者沒(méi)有設(shè)置參數(shù),就會(huì)Full Gc。
5、對(duì)象分配參數(shù):
開(kāi)啟JVM運(yùn)行參數(shù)顯示:-XX:+PrintGCDetails
設(shè)置分代年齡:-XX:MaxTenuringThreshold
設(shè)置年輕代比例:-XX:+UseAdaptiveSizePolicy(默認(rèn)開(kāi)啟),默認(rèn)8:1:1比例會(huì)自動(dòng)變化,如果想要保持8:1:1需開(kāi)啟:-XX:-UseAdaptiveSizePolicy
設(shè)置動(dòng)態(tài)年齡判斷比例:-XX:TargetSurvivorRatio
設(shè)置老年代空間分配擔(dān)保機(jī)制:-XX:-HandlePromotionFailure
設(shè)置大對(duì)象大小:-XX:PretenureSizeThreshold=10000 (單位是字節(jié)) -XX:+UseSerialGC,需要配合Serial或者ParNew垃圾回收器使用
三、垃圾標(biāo)記算法
1、可達(dá)性分析算法
把GC Roots對(duì)象作為起點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索有引用到的對(duì)象,找到的對(duì)象都標(biāo)記為非垃圾對(duì)象,其余未標(biāo)記的對(duì)象都是垃圾對(duì)象
GC Roots根節(jié)點(diǎn):線程棧的本地變量、靜態(tài)變量、本地方法棧的變量等
2、引用計(jì)數(shù)算法
給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它,計(jì)數(shù)器就加1,引用失效,計(jì)數(shù)器減1,計(jì)數(shù)器為0的對(duì)象就是垃圾對(duì)象,但有循環(huán)引用的問(wèn)題,比如圖中A、B兩個(gè)對(duì)象new出來(lái)的時(shí)候引用計(jì)數(shù)器都為1,然后B對(duì)象引用A的成員變量,A又引用B的成員變量,此時(shí)引用計(jì)數(shù)器都為2,最后兩個(gè)對(duì)象都賦null,都-1變?yōu)?。此時(shí)方法結(jié)束,內(nèi)存應(yīng)該回收,但是引用計(jì)數(shù)器為1,就導(dǎo)致內(nèi)存無(wú)法回收。
引用類型一般分為四種:強(qiáng)引用、軟引用、弱引用、虛引用
強(qiáng)引用被引用時(shí)不會(huì)被GC回收,其他引用就算被引用,GC的時(shí)候也可能會(huì)被回收掉。
強(qiáng)引用: 普通的變量引用,如new對(duì)象,被引用時(shí)不會(huì)被GC回收。
軟引用: 用SoftReference軟引用類型的對(duì)象包裹的對(duì)象,GC后發(fā)現(xiàn)釋放不出空間存放新的對(duì)象,則會(huì)把這些軟引用的對(duì)象回收掉。比如網(wǎng)頁(yè)中的回退操作,在當(dāng)前頁(yè)面打開(kāi)了新的頁(yè)面之后,然后又會(huì)退回當(dāng)前頁(yè)面,這時(shí),當(dāng)前頁(yè)面的對(duì)象就可以使用軟引用,當(dāng)GC的時(shí)候可以把這些可有可無(wú)的對(duì)象回收掉,并不會(huì)造成什么影響。
弱引用: 用WeakReference軟引用類型的對(duì)象包裹的對(duì)象,GC會(huì)直接回收掉。
public static WeakReference<User> user = new WeakReference<User>(new User());虛引用: 最弱的引用關(guān)系,GC會(huì)直接回收掉,幾乎不用。
對(duì)象的finalize()方法
對(duì)象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒(méi)有與GC Roots相連的引用鏈后會(huì)有兩次標(biāo)記
第一次標(biāo)記: 判斷對(duì)象沒(méi)有實(shí)現(xiàn)finalize()方法,沒(méi)有的話對(duì)象將直接被回收,有的話會(huì)進(jìn)行第二次標(biāo)記。
第二次標(biāo)記: 會(huì)執(zhí)行finalize()方法,我們可以通過(guò)在方法中重新與引用鏈上的任何的一個(gè)對(duì)象建立關(guān)聯(lián)進(jìn)行自救,比如把自己賦值給類變量或?qū)ο蟪蓡T變量,一個(gè)對(duì)象的finalize()方法只會(huì)被執(zhí)行一次。
總結(jié)
以上是生活随笔為你收集整理的JVM对象创建与内存分配机制学习总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: tiny4412 裸机程序 六、重定位代
- 下一篇: jitter单位_抖动(jitter)测