筆者最近學習jvm 的內(nèi)存模型,這里根據(jù)下面幾個模塊來做個總結(jié):
1.jvm的內(nèi)存模型
2.jvm gc原理
jvm的內(nèi)存模型
?jvm 的內(nèi)存模型實現(xiàn)很復雜渐扮,但是工作中經(jīng)常接觸的可大體分為如圖所示:
1.程序計數(shù)器:當前線程所執(zhí)行字節(jié)碼的行號指示器外构,它是線程私有的,占jvm的一小塊內(nèi)存,不會發(fā)生oom
2.虛擬機棧:它是描述線程調(diào)用方法的一個內(nèi)存模型顶别,它是由一個個棧幀組成,線程每調(diào)用一個方法形成一個棧幀拒啰,棧幀又由局部變量驯绎,操作數(shù)棧,動態(tài)鏈接谋旦,方法返回等信息組成剩失。線程執(zhí)行方法的過程意味著棧幀入棧和出棧的過程,它是線程私有的册着。由于棧的長度在jvm默認是動態(tài)增長的拴孤,所以一般情況下當擴容時內(nèi)存不足會發(fā)生oom
3.本地方法棧:和虛擬機棧相似,虛擬機棧是為虛擬機執(zhí)行Java方法提供服務指蚜,而本地方法棧是為虛擬機執(zhí)行native方法提供服務乞巧。
4.堆:主要用來存儲java對象,它是線程共享的摊鸡,堆按線程來劃分的話绽媒,可為每個線程劃分為一個個的區(qū)域。堆又個劃分成新生代和老人代免猾,堆的內(nèi)存大小可通過 -Xmx20m -Xms20m -Xmn10m參數(shù)來配置是辕,上述Xmx代表堆可用的最大內(nèi)存,Xms為堆初始內(nèi)存猎提,Xmn為新生代內(nèi)存获三,所以老人代內(nèi)存等于堆內(nèi)存減去老人代內(nèi)存 。新生代又可細分為eden區(qū),from survivor區(qū)疙教,to survivor區(qū)棺聊,新生代的可用內(nèi)存為eden區(qū)+survivor區(qū),jvm默認eden和survivor區(qū)的大小比例為8:1贞谓,可通過-XX:SurvivorRatio 或者-XX:NewRatio來配置限佩,內(nèi)存不足時會發(fā)生oom,jvm gc主要回收內(nèi)存的區(qū)域裸弦。
5.方法區(qū):主要用來存放類信息祟同,靜態(tài)變量,動態(tài)鏈接理疙,常量等信息的區(qū)域晕城,線程共享的,默認大小跟最大的堆內(nèi)存一致窖贤,可通過-XX:MaxperSize配置,當內(nèi)存不足時砖顷,也會發(fā)生oom,主要是常量池的oom
6.常量池:存放字面量和符號引用的區(qū)域主之,位于方法區(qū)中择吊。
JVM GC 原理
由于虛擬機棧所需的內(nèi)存在虛擬機啟動時就已經(jīng)基本確定了,而堆內(nèi)存和方法區(qū)的所需大小是不斷變動的槽奕,所以gc主要發(fā)生在這兩個區(qū)域當中几睛。當一個對象沒有被引用的時候,gc會將該對象回收粤攒,從而釋放內(nèi)存所森。
1.如何判斷對象是否可用
可通過引用計數(shù)器和可達性分析算法來判斷對象是否可用,jvm采用的是可達性分析算法來判斷對象是否可用夯接,這里先簡單介紹下引用計數(shù)器焕济,當一個對象被引用時,引用計數(shù)器就會加1盔几,當引用計數(shù)器為0時晴弃,則該對象變?yōu)椴豢捎茫窃趈ava中逊拍,會發(fā)生兩個不可用的對象互相引用的情況上鞠,導致這兩個對象不被回收,從而發(fā)生內(nèi)存泄漏芯丧。
可達性分析算法:jvm通過一系列的gc root芍阎,從該節(jié)點向下搜索,向下搜索的路徑被稱為引用鏈缨恒,當對象的引用鏈對任一gc root不可達時谴咸,從而判定該對象不可用轮听。
如何確定gc root?
1.虛擬機棧中引用的對象
2.方法區(qū)中類靜態(tài)熟悉引用的對象
3.方法區(qū)中常量引用的對象
4.本地方法棧中JNI引用的對象
對象的引用又分為強引用,軟引用岭佳,弱引用血巍,虛引用四種引用
強引用:類似Object x=new Obejct(),jvm gc時驼唱,不會回收此類的對象
軟引用:可用但不是必須的對象藻茂,當內(nèi)存不足時驹暑,可通過標記軟引用玫恳,在垃圾收集器回收時回收此類對象,可通過SoftReference聲明优俘。
弱引用:不是必須的對象京办,在垃圾收集器回收之前,會將該類對象回收帆焕,可通個WeakReference聲明
虛引用:此類對象只是用來標記惭婿,不會創(chuàng)建實例
2.GC算法
1.標記-清除算法:jvm gc時,會將不可用的對象全部標記叶雹,然后一次性的將標記的對象清除财饥,該算法實現(xiàn)簡單,但是標記和清除的效率低且會產(chǎn)生大量的內(nèi)存碎片折晦,會導致申請大內(nèi)存時發(fā)生full gc或者oom
2.復制算法:jvm 將可用的內(nèi)存分為大小相等兩塊钥星,只用其中的一塊,當發(fā)生gc時满着,將該塊存活著的對象全部復制到另一塊中谦炒,然后一次性清除該塊。效率高但是可用空間為內(nèi)存的一半风喇,該算法一般用于新生代中
3.標記-整理算法:標記過程和標記-清除算法一樣宁改,但是后續(xù)步驟不是對不可用對象進行回收,而是讓所有存活的對象移至以測魂莫,然后直接清理掉邊界以外的內(nèi)存