JVM內存模型與垃圾回收

Java開發有個很基礎的問題,雖然咱們平時接觸的很少,可是瞭解它卻成爲Java開發的必備基礎——這就是JVM。在C++中咱們須要手動申請內存而後釋放內存,不然就會出現對象已經再也不使用內存卻仍被佔用的狀況。在Java中JVM內置了垃圾回收的機制,幫助開發者承擔對象的建立和釋放的工做,極大的減輕了開發的負擔。那是否是咱們就不須要了解JVM了,顯然在作一些優化或者深刻研究應用性能的時候,JVM仍是起了很關鍵的做用的。所以本篇就總結性的描述下JVM的內存模型與垃圾回收相關的知識。html

本文的主要內容以下:java

  • 內存模型
  • 垃圾回收
  • 參考文章

內存模型

各部分的功能

這幾個存儲區最主要的就是棧區和堆區,那麼什麼是棧什麼是堆呢?說的簡單點,棧裏面存放的是基本的數據類型和引用,而堆裏面則是存放各類對象實例的。
算法

堆與棧分開設計是爲何呢?多線程

  • 棧存儲了處理邏輯、堆存儲了具體的數據,這樣隔離設計更爲清晰
  • 堆與棧分離,使得堆能夠被多個棧共享。
  • 棧保存了上下文的信息,所以只能向上增加;而堆是動態分配

棧的大小能夠經過-XSs設置,若是不足的話,會引發java.lang.StackOverflowError的異常併發

棧區

線程私有,生命週期與線程相同。每一個方法執行的時候都會建立一個棧幀(stack frame)用於存放 局部變量表、操做棧、動態連接、方法出口。性能

存放對象實例,全部的對象的內存都在這裏分配。垃圾回收主要就是做用於這裏的。優化

  • 堆得內存由-Xms指定,默認是物理內存的1/64;最大的內存由-Xmx指定,默認是物理內存的1/4。
  • 默認空餘的堆內存小於40%時,就會增大,直到-Xmx設置的內存。具體的比例能夠由-XX:MinHeapFreeRatio指定
  • 空餘的內存大於70%時,就會減小內存,直到-Xms設置的大小。具體由-XX:MaxHeapFreeRatio指定。

所以通常都建議把這兩個參數設置成同樣大,能夠避免JVM在不斷調整大小。spa

程序計數器

這裏記錄了線程執行的字節碼的行號,在分支、循環、跳轉、異常、線程恢復等都依賴這個計數器。線程

方法區

類型信息、字段信息、方法信息、其餘信息設計

總結

名稱 特徵 做用 配置 異常
棧區 線程私有,使用一段連續的內存空間 存放局部變量表、操做棧、動態連接、方法出口 -XSs StackOverflowError OutOfMemoryError
線程共享,生命週期與虛擬機相同 保存對象實例 -Xms -Xmx -Xmn OutOfMemoryError
程序計數器 線程私有、佔用內存小 字節碼行號
方法區 線程共享 存儲類加載信息、常量、靜態變量等 -XX:PermSize -XX:MaxPermSize OutOfMemoryError

垃圾回收

如何定義垃圾

有兩種方式,一種是引用計數(可是沒法解決循環引用的問題);另外一種就是可達性分析。

判斷對象能夠回收的狀況:

  • 顯示的把某個引用置位NULL或者指向別的對象
  • 局部引用指向的對象
  • 弱引用關聯的對象

垃圾回收的方法

Mark-Sweep標記-清除算法

這種方法優勢就是減小停頓時間,可是缺點是會形成內存碎片。

Copying複製算法

這種方法不涉及到對象的刪除,只是把可用的對象從一個地方拷貝到另外一個地方,所以適合大量對象回收的場景,好比新生代的回收。

Mark-Compact標記-整理算法

這種方法能夠解決內存碎片問題,可是會增長停頓時間。

Generational Collection 分代收集

最後的這種方法是前面幾種的合體,即目前JVM主要採起的一種方法,思想就是把JVM分紅不一樣的區域。每種區域使用不一樣的垃圾回收方法。

上面能夠看到堆分紅三個區域:

  • 新生代(Young Generation):用於存放新建立的對象,採用複製回收方法,若是在s0和s1之間複製必定次數後,轉移到年老代中。這裏的垃圾回收叫作minor GC;
  • 年老代(Old Generation):這些對象垃圾回收的頻率較低,採用的標記整理方法,這裏的垃圾回收叫作 major GC。
  • 永久代(Permanent Generation):存放Java自己的一些數據,當類再也不使用時,也會被回收。

這裏能夠詳細的說一下新生代複製回收的算法流程:

在新生代中,分爲三個區:Eden, from survivor, to survior。

  • 當觸發minor GC時,會先把Eden中存活的對象複製到to Survivor中;
  • 而後再看from survivor,若是次數達到年老代的標準,就複製到年老代中;若是沒有達到則複製到to survivor中,若是to survivor滿了,則複製到年老代中。
  • 而後調換from survivor 和 to survivor的名字,保證每次to survivor都是空的等待對象複製到那裏的。

垃圾回收器

串行收集器 Serial

這種收集器就是以單線程的方式收集,垃圾回收的時候其餘線程也不能工做。

並行收集器 Parallel

以多線程的方式進行收集

併發標記清除收集器 Concurrent Mark Sweep Collector, CMS

大體的流程爲:初始標記--併發標記--從新標記--併發清除

G1收集器 Garbage First Collector

大體的流程爲:初始標記--併發標記--最終標記--篩選回收

 

JVM裏的GC(Garbage Collection)的算法有不少種,如標記清除收集器,壓縮收集器,分代收集器等等,詳見HotSpot VM GC 的種類

       如今比較經常使用的是分代收集(generational collection,也是SUN VM使用的,J2SE1.2以後引入),即將內存分爲幾個區域,將不一樣生命週期的對象放在不一樣區域裏:young generationtenured generation和permanet generation。絕大部分的objec被分配在young generation(生命週期短),而且大部分的object在這裏die。當young generation滿了以後,將引起minor collection(YGC)。在minor collection後存活的object會被移動到tenured generation(生命週期比較長)。最後,tenured generation滿以後觸發major collection。major collection(Full gc)會觸發整個heap的回收,包括回收young generation。permanet generation區域比較穩定,主要存放classloader信息。

       young generation有eden、2個survivor 區域組成。其中一個survivor區域一直是空的,是eden區域和另外一個survivor區域在下一次copy collection後活着的objecy的目的地。object在survivo區域被複制直到轉移到tenured區。

       咱們要儘可能減小 Full gc 的次數(tenured generation 通常比較大,收集的時間較長,頻繁的Full gc會致使應用的性能收到嚴重的影響)。

堆內存GC
       JVM(採用分代回收的策略),用較高的頻率對年輕的對象(young generation)進行YGC,而對老對象(tenured generation)較少(tenured generation 滿了後才進行)進行Full GC。這樣就不須要每次GC都將內存中全部對象都檢查一遍。

非堆內存不GC

      GC不會在主程序運行期對PermGen Space進行清理,因此若是你的應用中有不少CLASS(特別是動態生成類,固然permgen space存放的內容不只限於類)的話,就極可能出現PermGen Space錯誤。

內存申請、對象衰老過程
1、內存申請過程

  1. JVM會試圖爲相關Java對象在Eden中初始化一塊內存區域;
  2. 當Eden空間足夠時,內存申請結束。不然到下一步;
  3. JVM試圖釋放在Eden中全部不活躍的對象(minor collection),釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區;
  4. Survivor區被用來做爲Eden及old的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,不然會被保留在Survivor區;
  5. 當old區空間不夠時,JVM會在old區進行major collection;
  6. 徹底垃圾收集後,若Survivor及old區仍然沒法存放從Eden複製過來的部分對象,致使JVM沒法在Eden區爲新對象建立內存區域,則出現"Out of memory錯誤";

2、對象衰老過程

  1. 新建立的對象的內存都分配自eden。Minor collection的過程就是將eden和在用survivor space中的活對象copy到空閒survivor space中。對象在young generation裏經歷了必定次數(能夠經過參數配置)的minor collection後,就會被移到old generation中,稱爲tenuring。
  2. GC觸發條件
    GC類型 觸發條件 觸發時發生了什麼 注意 查看方式
    YGC eden空間不足

    清空Eden+from survivor中全部no ref的對象佔用的內存
    將eden+from sur中全部存活的對象copy到to sur中
    一些對象將晉升到old中:
        to sur放不下的
        存活次數超過turning threshold中的
    從新計算tenuring threshold(serial parallel GC會觸發此項)

    從新調整Eden 和from的大小(parallel GC會觸發此項)

    全過程暫停應用
    是否爲多線程處理由具體的GC決定
    jstat –gcutil 
    gc log
    FGC

    old空間不足
    perm空間不足
    顯示調用System.GC, RMI等的定時觸發
    YGC時的悲觀策略
    dump live的內存信息時(jmap –dump:live)

    清空heap中no ref的對象
    permgen中已經被卸載的classloader中加載的class信息

    如配置了CollectGenOFirst,則先觸發YGC(針對serial GC)
    如配置了ScavengeBeforeFullGC,則先觸發YGC(針對serial GC)

    全過程暫停應用
    是否爲多線程處理由具體的GC決定

    是否壓縮須要看配置的具體GC
    jstat –gcutil 
    gc log
    permanent generation空間不足會引起Full GC,仍然不夠會引起PermGen Space錯誤。

參考 https://www.cnblogs.com/xing901022/p/7725961.html

        http://www.cnblogs.com/redcreen/archive/2011/05/04/2037056.html

相關文章
相關標籤/搜索