JVM結(jié)構(gòu)耕驰、GC工作機制詳解

JVM和GC爷辱,是Java的底層虛擬機和垃圾回收器,理解JVM和GC,對于我們理解Java很有幫助饭弓。在面試的過程中双饥,虛擬機和垃圾回收器也是常常被問及的內(nèi)容,故而了解JVM結(jié)構(gòu)和GC工作機制是很有必要的弟断。在網(wǎng)上找了相關(guān)的資料后咏花,這邊參照文章的內(nèi)容對于JVM結(jié)構(gòu)、GC工作機制解釋的很詳細阀趴,因此轉(zhuǎn)載過來昏翰,一方面是自己記憶,另一方面是方便大家刘急。
本文中主要分為四個部分的內(nèi)容:

  • JVM結(jié)構(gòu)
  • 內(nèi)存分配
  • 垃圾回收算法
  • 垃圾收集器

下面我們對上面四個部分的內(nèi)容棚菊,逐個了解。

一叔汁、JVM結(jié)構(gòu)

根據(jù)《java虛擬機規(guī)范》規(guī)定统求,JVM的基本結(jié)構(gòu)一般如下圖所示:


JVM基本結(jié)構(gòu)

Java文件運行解析流程

從JVM基本結(jié)構(gòu)圖可知,JVM主要包括四個部分:

1. 類加載器(ClassLoader)

在JVM啟動時或者在類運行時將需要的class加載到JVM中据块。(Java文件運行解析流程圖表示了從java源文件到JVM的整個過程码邻,可配合理解。 關(guān)于類的加載機制另假,可以參考深入分析ClassLoader

2. 執(zhí)行引擎

負責(zé)執(zhí)行class文件中包含的字節(jié)碼指令(執(zhí)行引擎的工作機制像屋,這里也不細說了,這里主要介紹JVM結(jié)構(gòu))浪谴;

3. 內(nèi)存區(qū)(也叫運行時數(shù)據(jù)區(qū))

在JVM運行的時候操作所分配的內(nèi)存區(qū)开睡。
運行時內(nèi)存區(qū)主要可以劃分為5個區(qū)域因苹,如圖:


運行時數(shù)據(jù)區(qū)區(qū)域劃分
  • 方法區(qū)(Method Area)
    用于存儲類結(jié)構(gòu)信息的地方苟耻,包括常量池、靜態(tài)變量扶檐、構(gòu)造函數(shù)等凶杖。雖然JVM規(guī)范把方法區(qū)描述為堆的一個邏輯部分, 但它卻有個別名non-heap(非堆)款筑,所以大家不要搞混淆了智蝠。方法區(qū)還包含一個運行時常量池。
  • java堆(Heap)
    存儲java實例或者對象的地方奈梳。這塊是GC的主要區(qū)域(后面解釋)杈湾。從存儲的內(nèi)容我們可以很容易知道,方法區(qū)和堆是被所有java線程共享的攘须。
  • java棧(JVM Stack)
    java椘嶙玻總是和線程關(guān)聯(lián)在一起,每當(dāng)創(chuàng)建一個線程時,JVM就會為這個線程創(chuàng)建一個對應(yīng)的java棧浮驳。在這個java棧中又會包含多個棧幀悍汛,每運行一個方法就創(chuàng)建一個棧幀,用于存儲局部變量表至会、操作棧离咐、方法返回值等。每一個方法從調(diào)用直至執(zhí)行完成的過程奉件,就對應(yīng)一個棧幀在java棧中入棧到出棧的過程宵蛀。所以java棧是現(xiàn)成私有的。
  • 程序計數(shù)器(PC Register)
    用于保存當(dāng)前線程執(zhí)行的內(nèi)存地址县貌。由于JVM程序是多線程執(zhí)行的(線程輪流切換)糖埋,所以為了保證線程切換回來后,還能恢復(fù)到原先狀態(tài)窃这,就需要一個獨立的計數(shù)器瞳别,記錄之前中斷的地方,可見程序計數(shù)器也是線程私有的杭攻。
  • 本地方法棧(Native Method Stack)
    和java棧的作用差不多祟敛,只不過是為JVM使用到的native方法服務(wù)的。

4. 本地方法接口

主要是調(diào)用C或C++實現(xiàn)的本地方法及返回結(jié)果兆解。

二馆铁、 內(nèi)存分配

在了解垃圾回收之前,得先了解JVM是怎么分配內(nèi)存的锅睛,然后識別哪些內(nèi)存是垃圾需要回收埠巨,最后才是用什么方式回收。
Java的內(nèi)存分配原理與C/C++不同现拒,C/C++每次申請內(nèi)存時都要malloc進行系統(tǒng)調(diào)用辣垒,而系統(tǒng)調(diào)用發(fā)生在內(nèi)核空間,每次都要中斷進行切換印蔬,這需要一定的開銷勋桶,而Java虛擬機是先一次性分配一塊較大的空間,然后每次new時都在該空間上進行分配和釋放侥猬,減少了系統(tǒng)調(diào)用的次數(shù)例驹,節(jié)省了一定的開銷,這有點類似于內(nèi)存池的概念退唠;二是有了這塊空間過后鹃锈,如何進行分配和回收就跟GC機制有關(guān)了。
java一般內(nèi)存申請有兩種:靜態(tài)內(nèi)存瞧预、動態(tài)內(nèi)存屎债。
靜態(tài)內(nèi)存
編譯時就能夠確定的內(nèi)存就是靜態(tài)內(nèi)存寨蹋,即內(nèi)存是固定的,系統(tǒng)一次性分配扔茅,比如int類型變量已旧;
動態(tài)內(nèi)存
動態(tài)內(nèi)存分配就是在程序執(zhí)行時才知道要分配的存儲空間大小,比如java對象的內(nèi)存空間召娜。

根據(jù)上面我們知道运褪,java棧、程序計數(shù)器玖瘸、本地方法棧都是線程私有的秸讹,線程生就生,線程滅就滅雅倒,棧中的棧幀隨著方法的結(jié)束也會撤銷璃诀,內(nèi)存自然就跟著回收了。所以這幾個區(qū)域的內(nèi)存分配與回收是確定的蔑匣,我們不需要管的劣欢。但是java堆和方法區(qū)則不一樣,我們只有在程序運行期間才知道會創(chuàng)建哪些對象裁良,所以這部分內(nèi)存的分配和回收都是動態(tài)的凿将。一般我們所說的垃圾回收也是針對的這一部分。

總之Stack的內(nèi)存管理是順序分配的价脾,而且定長牧抵,不存在內(nèi)存回收問題;而Heap 則是為java對象的實例隨機分配內(nèi)存侨把,不定長度犀变,所以存在內(nèi)存分配和回收的問題;

三秋柄、 垃圾檢測获枝、回收算法

垃圾收集器一般必須完成兩件事:檢測出垃圾;回收垃圾华匾。怎么檢測出垃圾映琳?一般有以下幾種方法:

引用計數(shù)法

給一個對象添加引用計數(shù)器,每當(dāng)有個地方引用它蜘拉,計數(shù)器就加1;引用失效就減1有鹿。
好了旭旭,問題來了,如果我有兩個對象A和B葱跋,互相引用持寄,除此之外源梭,沒有其他任何對象引用它們,實際上這兩個對象已經(jīng)無法訪問稍味,即是我們說的垃圾對象废麻。但是互相引用,計數(shù)不為0模庐,導(dǎo)致無法回收烛愧,所以還有另一種方法:

可達性分析算法:

以根集對象為起始點進行搜索,如果有對象不可達的話掂碱,即是垃圾對象怜姿。這里的根集一般包括java棧中引用的對象、方法區(qū)常量池中引用的對象疼燥、本地方法中引用的對象等沧卢。

總之,JVM在做垃圾回收的時候醉者,會檢查堆中的所有對象是否會被這些根集對象引用但狭,不能夠被引用的對象就會被垃圾收集器回收。一般回收算法也有如下幾種:

1.標記-清除(Mark-sweep)

算法和名字一樣,分為兩個階段:標記和清除胸蛛。標記所有需要回收的對象伯复,然后統(tǒng)一回收。這是最基礎(chǔ)的算法息罗,后續(xù)的收集算法都是基于這個算法擴展的。
不足:效率低才沧;標記清除之后會產(chǎn)生大量碎片迈喉。效果圖如下:

標記-清除(Mark-sweep)

2.復(fù)制(Copying)

此算法把內(nèi)存空間劃為兩個相等的區(qū)域,每次只使用其中一個區(qū)域温圆。垃圾回收時挨摸,遍歷當(dāng)前使用區(qū)域,把正在使用中的對象復(fù)制到另外一個區(qū)域中岁歉。此算法每次只處理正在使用中的對象得运,因此復(fù)制成本比較小,同時復(fù)制過去以后還能進行相應(yīng)的內(nèi)存整理锅移,不會出現(xiàn)“碎片”問題熔掺。當(dāng)然,此算法的缺點也是很明顯的非剃,就是需要兩倍內(nèi)存空間置逻。效果圖如下:


復(fù)制(Copying)

3. 標記-整理(Mark-Compact)

此算法結(jié)合了“標記-清除”和“復(fù)制”兩個算法的優(yōu)點。也是分兩階段备绽,第一階段從根節(jié)點開始標記所有被引用對象券坞,第二階段遍歷整個堆鬓催,把清除未標記對象并且把存活對象“壓縮”到堆的其中一塊,按順序排放恨锚。此算法避免了“標記-清除”的碎片問題宇驾,同時也避免了“復(fù)制”算法的空間問題。效果圖如下:

標記-整理(Mark-Compact)

(以上圖文摘自JVM調(diào)優(yōu)總結(jié)(三)-基本垃圾回收算法

4. 分代收集算法

這是當(dāng)前商業(yè)虛擬機常用的垃圾收集算法猴伶。分代的垃圾回收策略课舍,是基于這樣一個事實:不同的對象的生命周期是不一樣的。因此蜗顽,不同生命周期的對象可以采取不同的收集方式布卡,以便提高回收效率。

為什么要運用分代垃圾回收策略雇盖?在java程序運行的過程中忿等,會產(chǎn)生大量的對象,因每個對象所能承擔(dān)的職責(zé)不同所具有的功能不同所以也有著不一樣的生命周期崔挖,有的對象生命周期較長贸街,比如Http請求中的Session對象,線程狸相,Socket連接等薛匪;有的對象生命周期較短,比如String對象脓鹃,由于其不變類的特性逸尖,有的在使用一次后即可回收。試想瘸右,在不進行對象存活時間區(qū)分的情況下娇跟,每次垃圾回收都是對整個堆空間進行回收,那么消耗的時間相對會很長太颤,而且對于存活時間較長的對象進行的掃描工作等都是徒勞苞俘。因此就需要引入分治的思想,所謂分治的思想就是因地制宜龄章,將對象進行代的劃分吃谣,把不同生命周期的對象放在不同的代上使用不同的垃圾回收方式。

如何劃分做裙?將對象按其生命周期的不同劃分成:年輕代(Young Generation)岗憋、年老代(Old Generation)、持久代(Permanent Generation)菇用。其中持久代主要存放的是類信息澜驮,所以與java對象的回收關(guān)系不大,與回收息息相關(guān)的是年輕代和年老代惋鸥。這里有個比喻很形象

“假設(shè)你是一個普通的 Java 對象杂穷,你出生在 Eden 區(qū),在 Eden 區(qū)有許多和你差不多的小兄弟卦绣、小姐妹耐量,可以把 Eden 區(qū)當(dāng)成幼兒園,在這個幼兒園里大家玩了很長時間滤港。Eden 區(qū)不能無休止地放你們在里面廊蜒,所以當(dāng)年紀稍大,你就要被送到學(xué)校去上學(xué)溅漾,這里假設(shè)從小學(xué)到高中都稱為 Survivor 區(qū)山叮。開始的時候你在 Survivor 區(qū)里面劃分出來的的“From”區(qū),讀到高年級了添履,就進了 Survivor 區(qū)的“To”區(qū)屁倔,中間由于學(xué)習(xí)成績不穩(wěn)定,還經(jīng)常來回折騰暮胧。直到你 18 歲的時候锐借,高中畢業(yè)了,該去社會上闖闖了往衷。于是你就去了年老代钞翔,年老代里面人也很多。在年老代里席舍,你生活了 20 年 (每次 GC 加一歲)布轿,最后壽終正寢,被 GC 回收来颤。有一點沒有提汰扭,你在年老代遇到了一個同學(xué),他的名字叫愛德華 (慕光之城里的帥哥吸血鬼)脚曾,他以及他的家族永遠不會死东且,那么他們就生活在永生代”炯ィ”
具體區(qū)域可以通過VisualVM中的VisaulGC插件查看珊泳,如圖(openjdk 1.7):

GC分代劃分

年輕代:是所有新對象產(chǎn)生的地方。年輕代被分為3個部分——Enden區(qū)和兩個Survivor區(qū)(From和to)當(dāng)Eden區(qū)被對象填滿時拷沸,就會執(zhí)行Minor GC色查。并把所有存活下來的對象轉(zhuǎn)移到其中一個survivor區(qū)(假設(shè)為from區(qū))。Minor GC同樣會檢查存活下來的對象撞芍,并把它們轉(zhuǎn)移到另一個survivor區(qū)(假設(shè)為to區(qū))秧了。這樣在一段時間內(nèi),總會有一個空的survivor區(qū)序无。經(jīng)過多次GC周期后验毡,仍然存活下來的對象會被轉(zhuǎn)移到年老代內(nèi)存空間衡创。通常這是在年輕代有資格提升到年老代前通過設(shè)定年齡閾值來完成的。需要注意晶通,Survivor的兩個區(qū)是對稱的璃氢,沒先后關(guān)系,from和to是相對的狮辽。
年老代:在年輕代中經(jīng)歷了N次回收后仍然沒有被清除的對象一也,就會被放到年老代中,可以說他們都是久經(jīng)沙場而不亡的一代喉脖,都是生命周期較長的對象椰苟。對于年老代和永久代,就不能再采用像年輕代中那樣搬移騰挪的回收算法树叽,因為那些對于這些回收戰(zhàn)場上的老兵來說是小兒科舆蝴。通常會在老年代內(nèi)存被占滿時將會觸發(fā)Full GC,回收整個堆內(nèi)存。
持久代:用于存放靜態(tài)文件菱皆,比如java類须误、方法等。持久代對垃圾回收沒有顯著的影響仇轻。
分代回收的效果圖如下:
分代回收的效果圖

我這里之所以最后講分代京痢,是因為分代里涉及了前面幾種算法。
年輕代:涉及了復(fù)制算法篷店;
年老代:涉及了“標記-整理(Mark-Sweep)”的算法祭椰。

四、 垃圾收集器

垃圾收集算法是內(nèi)存回收的方法論疲陕,而實現(xiàn)這些方法論的則是垃圾收集器方淤。不同廠商不同版本JVM所提供的垃圾收集器可能不同,這里參照《深入理解Java虛擬機》說的是JDK1.7版本Hotspot虛擬機蹄殃,關(guān)于垃圾收集器有篇博文總結(jié)的不錯携茂,我就不說了,詳見:Java虛擬機學(xué)習(xí) - 垃圾收集器)

總結(jié)

雖然我不認為學(xué)習(xí)java必須去了解Java底層的實現(xiàn)诅岩,但是我想如果你更加理解JVM和GC的話讳苦,你就會更加理解Java,在以后的學(xué)習(xí)和工作中絕對受益匪淺吩谦。畢竟我們的目標不是刷墻工鸳谜,不是搬運工,而是開發(fā)攻城獅笆酵ⅰ咐扭!

參照:

JVM結(jié)構(gòu)、GC工作機制詳解
JVM原理講解和調(diào)優(yōu)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蝗肪,隨后出現(xiàn)的幾起案子袜爪,更是在濱河造成了極大的恐慌,老刑警劉巖穗慕,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饿敲,死亡現(xiàn)場離奇詭異妻导,居然都是意外死亡逛绵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門倔韭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來术浪,“玉大人,你說我怎么就攤上這事寿酌∫人眨” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵醇疼,是天一觀的道長硕并。 經(jīng)常有香客問我,道長秧荆,這世上最難降的妖魔是什么倔毙? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮乙濒,結(jié)果婚禮上陕赃,老公的妹妹穿的比我還像新娘。我一直安慰自己颁股,他們只是感情好么库,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甘有,像睡著了一般诉儒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亏掀,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天忱反,我揣著相機與錄音,去河邊找鬼幌氮。 笑死缭受,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的该互。 我是一名探鬼主播米者,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蔓搞?” 一聲冷哼從身側(cè)響起胰丁,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喂分,沒想到半個月后锦庸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蒲祈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年甘萧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梆掸。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡扬卷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酸钦,到底是詐尸還是另有隱情怪得,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布卑硫,位于F島的核電站徒恋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏欢伏。R本人自食惡果不足惜入挣,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颜懊。 院中可真熱鬧财岔,春花似錦、人聲如沸河爹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咸这。三九已至夷恍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間媳维,已是汗流浹背酿雪。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侄刽,地道東北人指黎。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像州丹,于是被迫代替她去往敵國和親醋安。 傳聞我的和親對象是個殘疾皇子杂彭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 原文閱讀 前言 這段時間懈怠了,罪過吓揪! 最近看到有同事也開始用上了微信公眾號寫博客了亲怠,挺好的~給他們點贊,這博客我...
    碼農(nóng)戲碼閱讀 5,948評論 2 31
  • 1.什么是垃圾回收柠辞? 垃圾回收(Garbage Collection)是Java虛擬機(JVM)垃圾回收器提供...
    簡欲明心閱讀 89,400評論 17 311
  • 一. 垃圾回收的意義 在C++中团秽,對象所占的內(nèi)存在程序結(jié)束運行之前一直被占用,在明確釋放之前不能分配給其它對...
    Stan_Z閱讀 1,921評論 0 25
  • 學(xué)習(xí)沒有舒服的叭首,學(xué)習(xí)的本質(zhì)就是脫離舒服习勤。如果你在舒服區(qū),那不叫學(xué)習(xí)放棒,那叫享受生活姻报。只要在學(xué)習(xí),那就是在進步的路上品...
    SUNNY看世界閱讀 586評論 0 2
  • 陽光灑在身上间螟,像極了一個渴望已久的擁抱。 我以為溫柔雖不能融化一切损肛,但能讓一對愛人暖暖相擁度過寒冬厢破;我以為...
    拾光的男人閱讀 278評論 4 4