JVM內存回收機制簡述

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

目前java的jdk默認虛擬機爲HotSpot,所以本文涉及虛擬機相關內容都指HotSpot虛擬機算法

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

判斷哪些對象可回收

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

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

如何回收

  找到可回收對象後,如何進行回收呢?併發

  內存回收算法主要有標記-清除、中止-複製、標記-整理,不一樣算法使用不一樣的場景,整體來講中止-複製算法適合對象存活時間短,存活率低的新生代,標記-清除和標記-整理算法適合對象存活時間長,存活率高的老年代高併發

  • 標記-清除(Mark-Sweep)
    經過可達性分析算法標記全部不可達對象,而後清理不可達對象。這種算法會造成大量的內存碎片
  • 中止-複製(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,不陌生吧哈哈)。從中止-複製算法的原理上咱們能夠看到,這種算法對於存活率較低的對象回收有着很是高的效率,並且不會造成內存碎片,可是會浪費必定的內存空間,適合對象存活率較低的新生代使用,若是在對象存活率較高的老年代採用這種算法,那將會是一場災難
  • 標記-整理(Mark-Compact)
    經過可達性分析算法標記全部不可達對象,而後將存活對象都向一個方向移動,而後清理掉邊界外的內存。這種算法是將存活對象向着一個方向彙集,而後將剩餘區域清空,這種算法適合對象存活率較高的老年代

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

JVM內存收集算法基本上都是採用分代收集算法,即將內存劃分爲新生代、老年代,也有人把方法區算作永久代spa

  • 新生代
    對象被建立時,內存分配都是發生在新生代(大對象直接分配在老年代),絕大多數對象都是朝生夕滅,建立後很快就會不在使用,變爲不可達的對象,被GC回收掉。新生代的對象存活率很低(到底有多低?研究代表有高達98%的對象建立後很快就消亡,想象一下平時編程,除了全局變量,局部變量在退出調用方法後還有幾個能存活),存活時間都很短,新生代發生的GC也叫作Minor GC,MinorGC發生頻率比較高(不必定等Eden區滿了才觸發)
  • 老年代
    當對象在新生代發生了屢次Minor GC後仍然存活的對象即進入老年代,老年代的對象比新生多不少,固然了內存比新生代也大不少(大概比例是1:2,即新生代佔用堆內存總量的1/3),當老年代內存滿時觸發Major GC即Full GC,Full GC發生頻率比較低,老年代對象存活時間比較長,存活率標記高

通常來講咱們所說的GC都是發生在新生代和老年代,新生代對象存活時間短,存活率低通常採用中止-複製算法,老年代對象存活時間長,存活率高,通常採用標記-整理、標記-清楚算法,具體採用何種算法和具體採用的垃圾收集器有關線程

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

GC收集器

GC採用分代回收算好後,起着重要做用的是GC收集器,GC收集器分爲新生代收集器和老年代收集器,不一樣的收集器使用不一樣的收集算法,有着不一樣的特色,因爲目前的收集器在內存回收時沒法消除(Stop-the-world),即在回收內存時不可避免的中止用戶線程,目前的收集器只能使停頓時間愈來愈短,可是沒法完全消除,主要的收集其中Parallel Scavenge和Parallel Old是追求吞吐量爲目標,其它的收集器都是追求高響應,低停頓,內存

  新生代收集器:Serial、PraNew、Parallel Scavenge

  老年代收集器:Serial Old、Parallel Old、CMS

  • Serial收集器(複製算法)
    新生代單線程收集器,標記和清理都是單線程,優勢是簡單高效。
  • Serial Old收集器(標記-整理算法)
    老年代單線程收集器,Serial收集器的老年代版本。
  • ParNew收集器(中止-複製算法)
    新生代收集器,能夠認爲是Serial收集器的多線程版本,在多核CPU環境下有着比Serial更好的表現。
  • Parallel Scavenge收集器(中止-複製算法)
    並行收集器,追求高吞吐量,高效利用CPU。吞吐量通常爲99%, 吞吐量= 用戶線程時間/(用戶線程時間+GC線程時間)。適合後臺應用等對交互相應要求不高的場景。
  • Parallel Old收集器(中止-複製算法)
    Parallel Scavenge收集器的老年代版本,並行收集器,吞吐量優先
  • CMS(Concurrent Mark Sweep)收集器(標記-清理算法)
    高併發、低停頓,追求最短GC回收停頓時間,cpu佔用比較高,響應時間快,停頓時間短,多核cpu 追求高響應時間的選擇
  • G1(Garbage-First)收集器(標記-整理算法、中止複製算法)GC最新型號,高富帥,高併發、可預測停頓、分代收集,不出意外將來主流將會逐步替代CMS,g1模糊了分代概念,雖然仍是分爲新生代和老年代,可是新生代和老年代再也不是物理隔離,而是將內存分爲n個region,以region爲清理單位,總體採用標記-整理算法,region內部使用中止-複製算法。
相關文章
相關標籤/搜索