1. JVM內(nèi)存管理機制
在進行Java程序設(shè)計時,一般不涉及內(nèi)存的分配和內(nèi)存回收的相關(guān)代碼,此處引用一句話:Java和C++之間存在一堵由內(nèi)存動態(tài)分配和垃圾收集技術(shù)所圍成的高墻,墻外的人想進去,墻里面的人想出來生音,個人從這兩句話中,捕獲到了兩個點跺株。
- java的自動內(nèi)存管理機制苍日,極大的節(jié)省了開發(fā)人員的精力,避免了易錯且復(fù)雜的內(nèi)存管理設(shè)計惰聂,相對于手動的內(nèi)存管理這是極大的飛躍疆偿。
- java自動內(nèi)存管理機制,其不能根據(jù)具體的場景提供最優(yōu)的內(nèi)存管理搓幌,其只提供普適的內(nèi)存管理機制杆故。想比于C++的手動內(nèi)存管理,靈活性不夠溉愁,存在制約系統(tǒng)性能的瓶頸处铛。
第二點是我們深入了解JVM內(nèi)存管理機制的意義,通過對原理的把握拐揭,在指定的場景下設(shè)計JVM最優(yōu)的內(nèi)存管理策略撤蟆,本文內(nèi)容組織結(jié)構(gòu)如下:
- JVM內(nèi)存分配
- JVM內(nèi)存回收
2. JVM內(nèi)存分配
ClassExample refereenceExample = new ClassExample ();
上述代碼,如果粗略劃分的話堂污,可以劃分為兩個過程:
- 在堆區(qū)分配對象內(nèi)存
- 將對象內(nèi)存地址賦值給對象引用
上述兩個過程基本就是內(nèi)存分配中家肯,比較重要的兩個知識點了,內(nèi)存分配策略和對象引用
2.1 堆區(qū)布局
對象分配在堆上這是毫無疑問的盟猖,如果在往下細分的話讨衣,那么堆區(qū)的內(nèi)存布局還是挺有講究的,大致可以分為如下布局:
- 新生代:從名字可見一般式镐,新生代區(qū)域中存放的對象一般是兩種:剛剛被new出來的對象反镇,或者經(jīng)歷內(nèi)存回收次數(shù)不多的對象。
- 老年代:老年代從名字也可見一般娘汞,一般存放兩種對象: 占用內(nèi)存比較多的大對象和經(jīng)歷過多次內(nèi)存回收過程的對象
- 永久代:永久代的內(nèi)存就比較固定歹茶,每個類的class對象,一般存放在永久代當(dāng)中。
上述JVM堆區(qū)中的內(nèi)存布局代表的是邏輯視圖惊豺,并不是實際的物理布局燎孟,實際上,JVM了提供多種不同的內(nèi)存分配和回收的策略尸昧,每種策略抽閑出邏輯視圖都會有細微的差別缤弦,但是上述邏輯視圖可以說是所有邏輯視圖的根視圖
2.2 內(nèi)存分配一般過程
在圖中有幾個重要的概念,需要著重強調(diào):
- 對象大小閾值設(shè)定彻磁,在JVM中可以通過設(shè)PretenureSizeThreshold這個參數(shù)碍沐,指定該閾值大小,如果待分配對象大小超過該閾值衷蜓,嘗試在老年代中開辟內(nèi)存空間存儲對象累提,否則嘗試在新生代中開辟內(nèi)存空間存儲對象
- MinorGC,這是JVM中一次內(nèi)存回收過程磁浇,內(nèi)存回收過程又稱垃圾回收過程斋陪,這次內(nèi)存回收是由新生代中內(nèi)存空間不足引起的,主要對新生代中的內(nèi)存對象進行回收
- FullGC置吓,這也是JVM中的一次內(nèi)存回收過程无虚,這次內(nèi)存回收過程是有老年代中內(nèi)存空間不足造成的,主要針對所有堆區(qū)中的內(nèi)存對象進行回收衍锚,包括新生代友题,老年代,以及永久代
- 對象年齡戴质,經(jīng)過一次內(nèi)存回收后依然存活的對象度宦,其年齡會加1。當(dāng)對象年齡超過一個指定閾值后告匠,其由新生代轉(zhuǎn)向老年代存儲戈抄。這個對象年齡的閾值,同樣可以通過設(shè)置JVM的MaxTenuringThreshold參數(shù)進行指定
2.3對象引用
JVM內(nèi)存區(qū)域的布局詳情中介紹了對象引用相關(guān)的內(nèi)容
3. JVM內(nèi)存回收
從程序員角度來看后专,內(nèi)存回收的過程是透明划鸽,具體細節(jié)都對程序員屏蔽了。JVM內(nèi)存區(qū)域的布局詳情戚哎,仔細的介紹了JVM中的內(nèi)存模型以及各個內(nèi)存區(qū)存儲的數(shù)據(jù)類型裸诽。
內(nèi)存回收主要針對的內(nèi)存區(qū)域主要是堆區(qū)和方法區(qū),在上文中談及了MinorGC以及FullGC建瘫。MinorGC主要是針對堆區(qū)進行內(nèi)存回收崭捍,F(xiàn)ullGC除了對堆區(qū)進行回收尸折,對方法區(qū)也進行回收啰脚,是相對重量級的回收動作。在內(nèi)存回收這個章節(jié)中,主要從以下三個方面闡述:
1. 什么樣的對象可回收橄浓?
解決方法:可達性分析
2. 如何回收內(nèi)存粒梦?
解決方法:標(biāo)記-清除算法,標(biāo)記-復(fù)制算法荸实,標(biāo)記-整理算法等內(nèi)存回收算法
3.什么時候回收合適匀们?
解決方法:安全點和安全區(qū)域
3.1 可達性分析
可達性分析是指:不能被GCROOT節(jié)點通過引用鏈達到的節(jié)點畔规,將被列入擬回收對象范圍局扶。對象6和對象7就不能被任何一個GCRoot節(jié)點所引用,是目標(biāo)回收對象叁扫。JVM認為這些對象不具備使用價值三妈,可以將其進行內(nèi)存回收。
根節(jié)點(GCROOT)是上文中提出的重要概念莫绣,一般來說畴蒲,如下對象可作為根節(jié)點對象:
(1)JVM虛擬機棧中引用的對象
(2)方法區(qū)中類靜態(tài)屬性引用對象
(3)方法區(qū)中類常量引用對象
(4)本機方法棧中引用對象
可達性分析是進行內(nèi)存回收的判定條件,在可達性分析之后对室,確定哪些對象是回收目標(biāo)饿凛。除了可達性分析之外,引用計數(shù)法同樣可用來進行判斷內(nèi)存回收目標(biāo)對象软驰,但是其無法解決循環(huán)引用問題涧窒。主流的JVM都采用可達性分析。
3.2內(nèi)存回收算法
通過可達性分析之后锭亏,將確定哪些對象是回收目標(biāo)纠吴,接著內(nèi)存回收算法將執(zhí)行具體的回收細節(jié)。下圖是內(nèi)存區(qū)域中三個狀態(tài)慧瘤,空閑內(nèi)存是未使用內(nèi)存戴已,目標(biāo)回收內(nèi)存是可達性分析之后可回收內(nèi)存,已占內(nèi)存是不需要回收內(nèi)存锅减。
- 標(biāo)記-清除算法
標(biāo)記-清除算法是比較直白的內(nèi)存回收算法糖儡,其直接釋放目標(biāo)回收內(nèi)存,沒有其他任何附加操作怔匣。該算法執(zhí)行后的結(jié)果如下:
標(biāo)記-清除算法執(zhí)行后的內(nèi)存圖
標(biāo)記-清除算法握联,邏輯簡單,易于理解,但是其有個致命的缺點就是其極易產(chǎn)生內(nèi)存碎片金闽,在分配大對象時纯露,不能有足夠連續(xù)的內(nèi)存空間而造成頻繁的GC。 - 標(biāo)記-復(fù)制算法
標(biāo)記-復(fù)制算法是針對內(nèi)存碎片問題提出的一種算法代芜。
image.png
在標(biāo)記-復(fù)制算法的設(shè)定中埠褪,始終有塊空白內(nèi)存區(qū)域,如上圖長內(nèi)存區(qū)域二挤庇。在內(nèi)存回收時钞速,將內(nèi)存區(qū)域一種存活對象全部按序復(fù)制到內(nèi)存區(qū)域二中后再對內(nèi)存區(qū)域一中所有對象進行回收。通過這種做法可以極大的避免內(nèi)存碎片嫡秕,但是空白內(nèi)存區(qū)域?qū)⑹冀K閑置玉工,內(nèi)存利用率不高。在上文中提及將堆區(qū)的內(nèi)存分為兩個surivior區(qū)以及Eden區(qū)就是采用標(biāo)記-復(fù)制算法淘菩。Eden區(qū)和surivior之間大小比例分配默認是8:1:1. - 標(biāo)記-整理算法
標(biāo)記-整理算法遵班,同樣是為解決內(nèi)存碎片提出的內(nèi)存回收算法,其在標(biāo)記-清楚算法的基礎(chǔ)上增加的整理功能潮改,對內(nèi)存對象進行拷貝移動狭郑,以保證空間內(nèi)存空間連續(xù)。
標(biāo)記-整理算法回收后內(nèi)存
標(biāo)記-整理算法汇在,能夠克服內(nèi)存碎皮翰萨,且不需要額外內(nèi)存,但是其拷貝和移動對象的時間消耗較高糕殉。該算法常用在老年代對象回收中亩鬼。
3.3 什么時候回收內(nèi)存合適
通過可達性分析,可知哪些對象是需要回收的對象阿蝶,在這過程中需要枚舉根節(jié)點雳锋,這是一個耗時操作,為了保證內(nèi)存回收的順利進行羡洁,必須保證引用關(guān)系的一致性玷过,即在內(nèi)存回收過程中對象引用關(guān)系不會發(fā)生變化。只有在這些地方進行內(nèi)存回收才是安全點筑煮,因此JVM引入了安全點的概念辛蚊,安全點處的對象引用關(guān)系不會改變,適合內(nèi)存回收真仲。安全區(qū)域是對安全點概念的擴充袋马,指在這一段區(qū)域內(nèi)對象引用關(guān)系具有一致性,能進行內(nèi)存回收秸应。