GC學習

java的內存大體能夠分爲5個區域:堆,方法區,jvm棧,本地方法棧,程序計數器java

 

程序計數器:主要用於記錄當前線程執行的字節碼執行到了第幾行。jvm

 

jvm棧:當每一個方法執行的時候都會建立一個棧幀(statck frame),棧幀裏面存儲了局部變量表、操做站、動態連接、方法出口等,局部變量表中存儲着方法的相關局部變量,包括各類基本數據類型,對象的引用,返回地址等。在這裏通常會有2種異常,當你的線程深度大於虛擬機規定的棧深度的時候,就會產生StatckOverFlowError(棧溢出)。可是通常虛擬的的棧均可以動態地擴展,當你擴展的大小大於分配的內存的時候就會拋出OutOfMemoryError(內存溢出)。測試

 

本地方法棧:它的實現和jvm棧相似,只是它執行的是native的方法,在不少虛擬機中,會將本地方法棧與虛擬機棧放在一塊兒使用。線程

 

:這是java最大的一塊內存區域,也是最重要的一塊內存區域,通常在這裏存放對象的實例,在邏輯上咱們要求堆內存是連續的。咱們能夠把它想象爲一個傳送帶,每當你建立一格對象的時候它就會日後移動一格,因此java中對象的建立速度很快。對象

 

方法區:在java虛擬機中,方法區被認爲是堆邏輯實現的一部分,在分代收集中把它定爲永久代。裏面主要存儲的是虛擬機已經加載的類信息,final常量、靜態變量、編譯器即時編譯的代碼等。運行時常量池是方法區的一部分,用於存儲編譯時就產生的字面常量,符號引用等。常量池也能夠存儲運行時生成的常量,好比字符串‘abc’已經在常量池中存在了,那麼直接返回字符串在常量池中的地址。blog

 

在java中一個對象的訪問方式內存

 

怎麼理解GC?字符串

要理解GC咱們首先要知道java的內存分配和回收機制,歸納來講就是分代分配,分代回收。編譯器

 

年輕代(Young Generation):對象被建立時,內存的分配首先發生在年輕代(大對象能夠直接被建立在年老代),大部分的對象在建立後很快就再也不使用,所以很快變得不可達,因而被年輕代的GC機制清理掉,這個GC機制被稱爲Minor GC或叫Young GC。注意,Minor GC並不表明年輕代內存不足,它事實上只表示在Eden區上的GC。虛擬機

  年輕代能夠被分爲3個區域:

  1. 絕大多數剛建立的對象會被分配在Eden區,其中的大多數對象很快就會消亡。Eden區是連續的內存空間,所以在其上分配內存極快;
  2. 最初一次,當Eden區滿的時候,執行Minor GC,將消亡的對象清理掉,並將剩餘的對象複製到一個存活區Survivor0(此時,Survivor1是空白的,兩個Survivor總有一個是空白的);
  3.  下次Eden區滿了,再執行一次Minor GC,將消亡的對象清理掉,將存活的對象複製到Survivor1中,而後清空Eden區;
  4.  將Survivor0中消亡的對象清理掉,將其中能夠晉級的對象晉級到Old區,將存活的對象也複製到Survivor1區,而後清空Survivor0區;
  5. 當兩個存活區切換了幾回(HotSpot虛擬機默認15次,用-XX:MaxTenuringThreshold控制,大於該值進入老年代,但這只是個最大值,並不表明必定是這個值)以後,仍然存活的對象(其實只有一小部分,好比,咱們本身定義的對象),將被複制到老年代。

  從上面的過程能夠看出,Eden區是連續的空間,且Survivor總有一個爲空。通過一次GC和複製,一個Survivor中保存着當前還活着的對象,而Eden區和另外一個Survivor區的內容都再也不須要了,能夠直接清空,到下一次GC時,兩個Survivor的角色再互換。所以,這種方式分配內存和清理內存的效率都極高,這種垃圾回收的方式就是著名的「中止-複製(Stop-and-copy)」清理法(將Eden區和一個Survivor中仍然存活的對象拷貝到另外一個Survivor中),這不表明着中止複製清理法很高效,其實,它也只在這種狀況下高效,若是在老年代採用中止複製,則效果會不好。

 

年老代(Old Generation):對象若是在年輕代存活了足夠長的時間而沒有被清理掉(即在幾回Young GC後存活了下來),則會被複制到年老代,年老代的空間通常比年輕代大,能存放更多的對象,在年老代上發生的GC次數也比年輕代少。當年老代內存不足時,將執行Major GC,也叫 Full GC。

 

垃圾回收機制基於的思想是對象必須是「活」的,這麼理解對象必須是「活」的呢?其實就是一組活的引用(GC roots),由於一個活的對象必然會被引用,所以咱們只要遍歷全部GC roots引用的對象,被引用到的對象就是活的。

  通常是

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象;
  • 方法區中類靜態屬性引用的對象;
  • 方法區中常量引用的對象;
  • 本地方法棧中JNI(即通常說的Native方法)引用的對象;

  總結就是,方法運行時,方法中引用的對象;類的靜態變量引用的對象;類中常量引用的對象;Native方法中引用的對象。

  被稱爲是gc roots。

找到了存活的對象要怎麼處理了,JVM虛擬機採用了一種自適應的技術:

1.中止複製(stop-and-copy):jvm虛擬機會暫停程序的運行,而後把存活的對象從舊堆複製到新堆,並使它們緊密地排列,可是這樣會有一個問題,就是勢必要給jvm虛擬機2倍的內存才能幹這種事情。某些jvm虛擬機地作法是在堆裏面分配2塊較大地內存。還有一個問題就是,通常咱們產生的垃圾不會特別多,咱們把全部的對象複製過去會很浪費。

2.標記清掃(mark-and-sweep):它的思路和前面同樣,遍歷全部的引用,找出存活的對象打上一個標記,當全部對象都打完標記以後,把打上標記的對象清楚掉。

 

我遇到的問題,測試機升級了16G內存的時候,xms和xmx設置爲10G出現了GC out of memory的問題。

xms的意思就是堆內存的初始分配空間,xmx的意思就是堆內存的最大分配空間。當可用堆內存小於40%時會設置到xmx,可用堆內存大於70%時會設置到xms。通常建議xms=xmx=內存的70%-75%,爲何要設置同樣,由於當xms很小的時候會頻繁地觸發GC後調整堆內存的大小,這樣很浪費。還有一個要注意設置的是新生代和老年代的大小,新生代的意思是全部新建立的對象都會在這裏,儘可能把新生代設置地大一點,由於若是設置地比較小的話就會頻繁地執行Minor GC。可是也不能太大,由於太大會致使老年代很小,老年代過小會引發堆內存碎片的問題。

相關文章
相關標籤/搜索