JVM內(nèi)存分配和垃圾收集

參考:
周志明 《深入理解Java虛擬機》
http://www.cnblogs.com/dolphin0520/p/3613043.html

1 JVM內(nèi)存區(qū)域劃分

1. Java程序的執(zhí)行過程

首先,Java源代碼文件(.java后綴)會被Java編譯器編譯為字節(jié)碼文件(.class后綴),然后由JVM中的類加載器加載各個類的字節(jié)碼文件,加載完畢之后,交由JVM執(zhí)行引擎執(zhí)行。在整個程序執(zhí)行過程中,JVM會用一段空間來存儲程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息,這段空間一般被稱作為Runtime Data Area(運行時數(shù)據(jù)區(qū)),也就是我們常說的JVM內(nèi)存。因此,在Java中我們常常說到的內(nèi)存管理就是針對這段空間進行管理(如何分配和回收內(nèi)存空間)。如圖所示:


QQ截圖20160301223335.png
2. 運行時數(shù)據(jù)區(qū)的結(jié)構(gòu)

根據(jù)《Java虛擬機規(guī)范》的規(guī)定,運行時數(shù)據(jù)區(qū)通常包括這幾個部分:程序計數(shù)器(Program Counter Register)、Java棧(VM Stack)、本地方法棧(Native Method Stack)、方法區(qū)(Method Area)、堆(Heap)。

QQ截圖20160301223335.png
  1. 程序計數(shù)器
    程序計數(shù)器(Program Counter Register),也有稱作為PC寄存器。想必學(xué)過匯編語言的朋友對程序計數(shù)器這個概念并不陌生,在匯編語言中,程序計數(shù)器是指CPU中的寄存器,它保存的是程序當前執(zhí)行的指令的地址(也可以說保存下一條指令的所在存儲單元的地址),當CPU需要執(zhí)行指令時,需要從程序計數(shù)器中得到當前需要執(zhí)行的指令所在存儲單元的地址,然后根據(jù)得到的地址獲取到指令,在得到指令之后,程序計數(shù)器便自動加1或者根據(jù)轉(zhuǎn)移指針得到下一條指令的地址,如此循環(huán),直至執(zhí)行完所有的指令。
    雖然JVM中的程序計數(shù)器并不像匯編語言中的程序計數(shù)器一樣是物理概念上的CPU寄存器,但是JVM中的程序計數(shù)器的功能跟匯編語言中的程序計數(shù)器的功能在邏輯上是等同的,也就是說是用來指示 執(zhí)行哪條指令的。
    由于在JVM中,多線程是通過線程輪流切換來獲得CPU執(zhí)行時間的,因此,在任一具體時刻,一個CPU的內(nèi)核只會執(zhí)行一條線程中的指令,因此,為了能夠使得每個線程都在線程切換后能夠恢復(fù)在切換之前的程序執(zhí)行位置,每個線程都需要有自己獨立的程序計數(shù)器,并且不能互相被干擾,否則就會影響到程序的正常執(zhí)行次序。因此,可以這么說,程序計數(shù)器是每個線程所私有的。
    在JVM規(guī)范中規(guī)定,如果線程執(zhí)行的是非native方法,則程序計數(shù)器中保存的是當前需要執(zhí)行的指令的地址;如果線程執(zhí)行的是native方法,則程序計數(shù)器中的值是undefined。
    由于程序計數(shù)器中存儲的數(shù)據(jù)所占空間的大小不會隨程序的執(zhí)行而發(fā)生改變,因此,對于程序計數(shù)器是不會發(fā)生內(nèi)存溢出現(xiàn)象(OutOfMemory)的。
  2. Java 棧
    Java棧也稱作虛擬機棧(Java Vitual Machine Stack),也就是我們常常所說的棧,跟C語言的數(shù)據(jù)段中的棧類似。事實上,Java棧是Java方法執(zhí)行的內(nèi)存模型。
    erer.png

    局部變量表,顧名思義,想必不用解釋大家應(yīng)該明白它的作用了吧。就是用來存儲方法中的局部變量(包括在方法中聲明的非靜態(tài)變量以及函數(shù)形參)。對于基本數(shù)據(jù)類型的變量,則直接存儲它的值,對于引用類型的變量,則存的是指向?qū)ο蟮囊谩>植孔兞勘淼拇笮≡诰幾g器就可以確定其大小了,因此在程序執(zhí)行期間局部變量表的大小是不會改變的。
    操作數(shù)棧,想必學(xué)過數(shù)據(jù)結(jié)構(gòu)中的棧的朋友想必對表達式求值問題不會陌生,棧最典型的一個應(yīng)用就是用來對表達式求值。想想一個線程執(zhí)行方法的過程中,實際上就是不斷執(zhí)行語句的過程,而歸根到底就是進行計算的過程。因此可以這么說,程序中的所有計算過程都是在借助于操作數(shù)棧來完成的。
    指向運行時常量池的引用,因為在方法執(zhí)行的過程中有可能需要用到類中的常量,所以必須要有一個引用指向運行時常量。
    方法返回地址,當一個方法執(zhí)行完畢之后,要返回之前調(diào)用它的地方,因此在棧幀中必須保存一個方法返回地址。
    由于每個線程正在執(zhí)行的方法可能不同,因此每個線程都會有一個自己的Java棧,互不干擾。
  3. 本地方法棧
    本地方法棧與Java棧的作用和原理非常相似。區(qū)別只不過是Java棧是為執(zhí)行Java方法服務(wù)的,而本地方法棧則是為執(zhí)行本地方法(Native Method)服務(wù)的。在JVM規(guī)范中,并沒有對本地方發(fā)展的具體實現(xiàn)方法以及數(shù)據(jù)結(jié)構(gòu)作強制規(guī)定,虛擬機可以自由實現(xiàn)它。在HotSopt虛擬機中直接就把本地方法棧和Java棧合二為一。

  4. Java中的堆是用來存儲對象本身的以及數(shù)組(當然,數(shù)組引用是存放在Java棧中的)。只不過和C語言中的不同,在Java中,程序員基本不用去關(guān)心空間釋放的問題,Java的垃圾回收機制會自動進行處理。因此這部分空間也是Java垃圾收集器管理的主要區(qū)域。另外,堆是被所有線程共享的,在JVM中只有一個堆。
  5. 方法區(qū)
    方法區(qū)在JVM中也是一個非常重要的區(qū)域,它與堆一樣,是被線程共享的區(qū)域。在方法區(qū)中,存儲了每個類的信息(包括類的名稱、方法信息、字段信息)、靜態(tài)變量、常量以及編譯器編譯后的代碼等。
    在Class文件中除了類的字段、方法、接口等描述信息外,還有一項信息是常量池,用來存儲編譯期間生成的字面量和符號引用。
    在方法區(qū)中有一個非常重要的部分就是運行時常量池,它是每一個類或接口的常量池的運行時表示形式,在類和接口被加載到JVM后,對應(yīng)的運行時常量池就被創(chuàng)建出來。當然并非Class文件常量池中的內(nèi)容才能進入運行時常量池,在運行期間也可將新的常量放入運行時常量池中,比如String的intern方法。
    在JVM規(guī)范中,沒有強制要求方法區(qū)必須實現(xiàn)垃圾回收。很多人習慣將方法區(qū)稱為“永久代”,是因為HotSpot虛擬機以永久代來實現(xiàn)方法區(qū),從而JVM的垃圾收集器可以像管理堆區(qū)一樣管理這部分區(qū)域,從而不需要專門為這部分設(shè)計垃圾回收機制。不過自從JDK7之后,Hotspot虛擬機便將運行時常量池從永久代移除了。

3.垃圾收集

  1. 方法區(qū)垃圾收集:收集無用的類和常量。判定一個類已經(jīng)廢棄需要三個條件:
  • 該類所有的實例已經(jīng)被回收
  • 加載該類的ClassLoader已經(jīng)被回收
  • 該類對應(yīng)的Class類對象沒有被引用,沒有代碼通過反射訪問該類的方法。
  1. 判斷堆中的對象死亡:
  • 引用計數(shù)法:通過一個計數(shù)器來記錄對象被引用的次數(shù),無法解決循環(huán)引用的問題
  • 根搜索算法: 選取一些對象作為 GC-Roots, 進行引用搜索,當判定一個對象進行根搜索無法到達的時候,說明該對象已經(jīng)沒有被引用。可作為GC-Roots的對象包括:
    a. 虛擬機棧中引用的對象
    b. 方法區(qū)中類的靜態(tài)屬性引用的對象
    c. 方法區(qū)中常量引用的對象
    d. 本地方法棧中引用的對象
  • 兩次標記:在根搜索中得到的沒有引用的對象不是立即被標記成可回收對象的,而是先進行一次標記,假如對象覆蓋了finalize() 方法,則放進一個隊列F-Queue中,由一個虛擬機創(chuàng)建的低優(yōu)先級Finalizer執(zhí)行對象的finalize()方法,如果對象的這個方法里成功建立了對自己的引用,則會逃離本次垃圾收集。需要注意的是,每個對象的finalize()方法僅會被執(zhí)行一次,也就是說對象自救一次后再次被標記則無法通過finalize()方法自救。
  1. 垃圾收集算法
    虛擬機采用分代收集的機制。一般是根據(jù)對象的存貨周期分為新生代和老年代。
  • 標記-清除算法:顧名思義,先標記后清除。將導(dǎo)致內(nèi)存的碎片化。
  • 復(fù)制算法:在新生代區(qū)域大多數(shù)對象都是“朝生暮死”的前提下,將新生代堆分為Eden區(qū)和兩個較小的Survivor區(qū), 每次使用Eden區(qū)和其中一塊Survivor區(qū),垃圾收集的時候?qū)⒋婊顚ο髲?fù)制到另外一塊Survivor區(qū)中,然后清理掉Eden和使用過的Survivor。
  • 標記-整理算法:老年代的對象成活率較高,使用復(fù)制操作的話效率很低。標記整理算法在對對象進行標記之后不是直接清除,而是將存活的對象向一端移動,最后清除掉邊界之外的內(nèi)存。
  1. 內(nèi)存分配和回收策略
  • 對象優(yōu)先在Eden區(qū)分配。當Eden區(qū)沒有足夠空間的時候,系統(tǒng)發(fā)起一次MinorGC(新生代回收)
  • 大對象直接分配在老年代。比如長字符串和數(shù)組。-XX:PretenureSizeThreshold參數(shù)可以設(shè)置直接分配進入老年代對象的大小分界線。
  • 長期存活的對象將進入老年代。每個對象都有一個age計數(shù)器,如果Eden對象經(jīng)過MinorGC沒有被收集,根據(jù)復(fù)制算法將進入Survivor區(qū),age為1,此后該對象每次熬過MinorGC,age+=1。當age增加到一定程度(默認15),將會晉升至老年代。該age參數(shù)可以通過 -XX:MaxTenuringThreshold 進行設(shè)置。
  • 動態(tài)對象年齡判定:如果Survivor空間中相同年齡的對象大小總和大于了Survivor空間的一半,那么年齡大于或等于該年齡的對象將直接進入老年代。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

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