java jvm基礎知識

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

        :http://lhc1986.iteye.com/blog/1421832

  :http://www.cnblogs.com/xhr8334/archive/2011/12/01/2270994.html

JVM內存回收機制簡述



JVM內存回收機制涉及的知識點太多了,瞭解越多越迷糊,汗一個,這裏僅簡單做個筆記,主要參考《深入理解Java虛擬機:JVM高級特性與最佳實踐(第二版)》


目前java的jdk默認虛擬機爲HotSpot,因此本文涉及虛擬機相關內容都指HotSpot虛擬機


本文主要關注GC的回收:判斷哪些對象可回收,如何回收,回收機制


1、判斷哪些對象可回收


GC是通過對象是否存活來決定是否進行回收,判斷對象是否存活主要有兩種算法:引用計數算法、可達性分析算法


1.1、引用計數算法
          引用計數的算法原理是給對象添加一個引用計數器,每被引用一次計數器加1,引用失效時減1,當計數器0後表示對象不在被引用,可以被回收了,引用計數法簡單高效,
      但是存在對象之間循環引用問題,可能導致無法被GC回收,需要花很大精力去解決循環引用問題
1.2、可達性分析算法
          可達性分析的算法原理是從對象根引用(堆棧、方法表的靜態引用和常量引用區、本地方法棧)開始遍歷搜索所有可到達對象,形成一個引用鏈,遍歷的同時標記出可達
      對象和不可達對象,不可達對象表示沒有任何引用存在,可以被GC回收
2、如何回收


        找到可回收對象後,如何進行回收呢?
      內存回收算法主要有標記-清除、停止-複製、標記-整理,不同算法使用不同的場景,總體來說停止-複製算法適合對象存活時間短,存活率低的新生代,標記-清除和標記-整理算法
      適合對象存活時間長,存活率高的老年代。


2.1、標記-清除(Mark-Sweep)
          通過可達性分析算法標記所有不可達對象,然後清理不可達對象。這種算法會形成大量的內存碎片
2.2、停止-複製(Stop-Copy)
          將新生代內存按照8:1:1的比例分爲一個eden區和兩個survivor(survivor0,survivor1)區,回收時先將eden區存活對象複製到一個survivor0區,然後清空eden區,
      當這個survivor0區也存放滿了時,則將eden區和survivor0區存活對象複製到另一個survivor1區,然後清空eden和這個survivor0區,此時survivor0區是空的,然後將
      survivor0區和survivor1區交換,即保持survivor1區爲空, 如此往復,當survivor1區不足以存放 eden和survivor0的存活對象時,就將存活對象直接存放到老年代(
      這時我們可能回想,若是老年代也滿了咋辦,若是老年代也滿了就會觸發一次Full GC,也就是新生代、老年代都進行回收,若是內存還不夠呢。。。,還不夠那不廢話了
      嗎,OutOfMemory,不陌生吧哈哈)。從停止-複製算法的原理上我們可以看到,這種算法對於存活率較低的對象回收有着非常高的效率,而且不會形成內存碎片,但是會浪
      費一定的內存空間,適合對象存活率較低的新生代使用,如果在對象存活率較高的老年代採用這種算法,那將會是一場災難。
2.3、標記-整理(Mark-Compact)
          通過可達性分析算法標記所有不可達對象,然後將存活對象都向一個方向移動,然後清理掉邊界外的內存。這種算法是將存活對象向着一個方向聚集,然後將剩餘區域
      清空,這種算法適合對象存活率較高的老年代


3、GC回收機制:分代收集算法

     


          JVM內存收集算法基本上都是採用分代收集算法,即將內存劃分爲新生代、老年代,也有人把方法區算做永久代
      
     方法區(永久代):permanent space
     青年代(新生代):eden+fromspace(survivor0)+tospace(survivor1)
     老年代(tenured):old space(tenured space)


3.1、新生代
          對象被創建時,內存分配都是發生在新生代(大對象直接分配在老年代),絕大多數對象都是朝生夕滅,創建後很快就會不在使用,變爲不可達的對象,被GC回收掉。新生
      代的對象存活率很低(到底有多低?研究表明有高達98%的對象創建後很快就消亡,想象一下平時編程,除了全局變量,局部變量在退出調用方法後還有幾個能存活),存活時間
      都很短,新生代發生的GC也叫做Minor GC,MinorGC發生頻率比較高(不一定等Eden區滿了才觸發)
3.2、老年代
          當對象在新生代發生了多次Minor GC後仍然存活的對象即進入老年代,老年代的對象比新生多很多,當然了內存比新生代也大很多(大概比例是1:2,即新生代佔用堆內存
      總量的1/3),當老年代內存滿時觸發Major GC即Full GC,Full GC發生頻率比較低,老年代對象存活時間比較長,存活率標記高,一般來說我們所說的GC都是發生在新生代和
      老年代,新生代對象存活時間短,存活率低一般採用停止-複製算法,老年代對象存活時間長,存活率高,一般採用標記-整理、標記-清楚算法,具體採用何種算法和具體採用
      的垃圾收集器有關。

3.3、GC觸發的條件

GC類型 觸發條件 觸發時發生了什麼 注意 查看方式
YGC eden空間不足 1、清空eden+survivor0(from survivor)中所有非引用對象所佔內存
將Eden+from survivor中所有存活的對象copy到to suvivor中
2、一些對象將晉升到old中:
2.1、to survivor中放不下的
2.2、存活次數超過turning threshold中的
重新計算tenuring threshold(serial parallel GC會觸發此項)
全過程暫停應用是否爲多線程處理由具體的GC決定 jstat -gcutil gc log
FGC old空間不足
perm空間不足
顯示調用system.GC,RMI等定時觸發
YGC時的悲觀策略
dump live的內存信息時(jmap -dump:live)
1、清空heap中no ref的對象
2、permgen中已經被卸載的classloader中加載的class信息
如配置了CollectGenOFirst,則先觸發YGC(針對serial GC)
如配置了ScavengeBeforeFullGC,則先觸發YGC(針對serial GC)
全過程暫停應用是否爲多線程處理由具體的GC決定
是否壓縮需要看配置的具體GC
jstat -gcutil gc log

     
 
GC類型 觸發條件 觸發時發生了什麼 注意 查看方式   
YGC eden空間不足 1、清空eden+survivor0(from survivor)中所有非引用對象所佔內存
將Eden+from survivor中所有存活的對象copy到to suvivor中
2、一些對象將晉升到old中:
2.1、to survivor中放不下的
2.2、存活次數超過turning threshold中的
重新計算tenuring threshold(serial parallel GC會觸發此項) 全過程暫停應用是否爲多線程處理由具體的GC決定 jstat -gcutil gc log   
FGC old空間不足
perm空間不足
顯示調用system.GC,RMI等定時觸發
YGC時的悲觀策略
dump live的內存信息時(jmap -dump:live) 1、清空heap中no ref的對象
2、permgen中已經被卸載的classloader中加載的class信息
如配置了CollectGenOFirst,則先觸發YGC(針對serial GC)
如配置了ScavengeBeforeFullGC,則先觸發YGC(針對serial GC) 全過程暫停應用是否爲多線程處理由具體的GC決定
是否壓縮需要看配置的具體GC jstat -gcutil gc log  


     
3.4、形象說明,有助理解
          GC裏面有些類似未成年人和成年人,新創建的對象爲新生代,新生代想要成爲老年代需要經過一定的成長(總得一點點長大是吧),新創建的對象年齡爲1,每發生一次
      Minor GC,存活對象的年齡增加1,當經歷了15次Minor GC後,仍然存活的對象達到15歲,成達到法定成年年齡15歲(默認是15),正式成爲成年人(老年代),對象成年後也
      就沒有了年齡概念,直到對象死亡,會一直呆在老年代,當然也有一些老不死的(靜態變量、常量等),會與世長存,除非地球滅亡(GC崩潰)。


4、GC收集器


          GC採用分代回收算好後,起着重要作用的是GC收集器,GC收集器分爲新生代收集器和老年代收集器,不同的收集器使用不同的收集算法,有着不同的特點,由於目前的
      收集器在內存回收時無法消除(Stop-the-world),即在回收內存時不可避免的停止用戶線程,目前的收集器只能使停頓時間越來越短,但是無法徹底消除,主要的收集其中
      Parallel Scavenge和Parallel Old是追求吞吐量爲目標,其它的收集器都是追求高響應,低停頓:
          新生代收集器:Serial、PraNew、Parallel Scavenge
          老年代收集器:Serial Old、Parallel Old、CMS


4.1、Serial收集器(複製算法)
      新生代單線程收集器,標記和清理都是單線程,優點是簡單高效。
4.2、Serial Old收集器(標記-整理算法)
      老年代單線程收集器,Serial收集器的老年代版本。
4.3、ParNew收集器(停止-複製算法)
      新生代收集器,可以認爲是Serial收集器的多線程版本,在多核CPU環境下有着比Serial更好的表現。
4.4、Parallel Scavenge收集器(停止-複製算法)
      並行收集器,追求高吞吐量,高效利用CPU。吞吐量一般爲99%, 吞吐量= 用戶線程時間/(用戶線程時間+GC線程時間)。適合後臺應用等對交互相應要求不高的場景。
4.5、Parallel Old收集器(停止-複製算法)
      Parallel Scavenge收集器的老年代版本,並行收集器,吞吐量優先
4.6、CMS(Concurrent Mark Sweep)收集器(標記-清理算法)
      高併發、低停頓,追求最短GC回收停頓時間,cpu佔用比較高,響應時間快,停頓時間短,多核cpu 追求高響應時間的選擇
4.7、G1(Garbage-First)收集器(標記-整理算法、停止複製算法)
         GC最新型號,高富帥,高併發、可預測停頓、分代收集,不出意外未來主流將會逐步替代CMS,g1模糊了分代概念,雖然還是分爲新生代和老年代,但是新生代和老年代
     不再是物理隔離,而是將內存分爲n個region,以region爲清理單位,整體採用標記-整理算法,region內部使用停止-複製算法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章