JVM的內存分配和垃圾回收機制

很多的時間大家都很迷茫。但是想想要走的路,你會發現一切又是那麼的清晰。
   今天看了一點JVM的第三章,感覺自己理解的不是很透徹。所以還是寫點東西來加深點印象吧。
1、概述
   由於之前我們將java中的內存大致的分爲了堆和棧。所以涉及到內存釋放的問題主要就是指這兩方面。之前我們介紹道程序計數器、虛擬機棧、本地方法棧都是屬於棧的,並且都是線程隔離的。是隨線性而生,隨線程而死的。但是java的堆和方法區是堆類型的,是線程共享的。所以是需要gc垃圾回收的,垃圾回收機制關注的就是這部分的內存回收。
2、對象的生與死
   在堆裏面存活了幾乎所有的對象實例。在收集垃圾之前我們需要知道哪些對象是活的哪些是死的。
   第一種方法是引用計數法。給每個對象添加一個引用計數器。每當有調用的時候就加1,當引用失效的時候就減1。任意時刻計時器的值是0表示該對象不需要使用了就可以清除。不過hotspot並沒有使用這種方案。
   java中使用的是可達性分析算法。從一系列稱爲GC Roots的對象作爲起點,然後從這些起點開始向下搜索。當無法搜索到的對象就可以將其作爲清除的對象。那麼,GC Roots的對象是怎麼確定的?
在java語言中,可以作爲GC Roots對象的有:
1 虛擬機棧(棧幀中的本地變量表)中引用的對象。
2 方法區中類靜態屬性引用的對象。
3 方法區中常用引用的對象。
4 本地方法棧中JNI引用的對象。


  這裏在說說引用。如果reference類型的數據中存儲的數值代表的是另外一塊內存的起始地址,就稱這塊內存代表着一個引用。java中引用又分爲四種:強引用、軟引用、弱引用、虛引用。


  要宣佈一個對象是否死亡,至少要經歷兩次的標記過程。第一次將不滿足可達性分析的沒有與GC Roots相連的對象進行標記和篩選。(篩選的條件是是否需要執行finalize())。之後JVM會將這些篩選的對象全部放置在一個F隊列中。之後在這個F隊列中進行第二次篩選,這時候被選上的對象就可以回收了。
注意:任何對象的finalize()方法只執行一次。
 我們知道在hotspot中方法區也屬於堆了。但是要知道該區域回收的性價比很低。主要回收的是:廢棄的常量和無用的類。對於在方法區中頻繁自定義類加載器的場景都需要虛擬機具備類的卸載的功能。


3 垃圾收集算法
  a 標記-清除算法。
  主要分爲標記和清除兩部分。主要的不足是:執行的效率不高。第二個就是產生了大量的不連續的內存碎片。碎片太多可能會導致不能產生大一點的對象。
 b 複製算法
  它主要是將可用的內存按容量劃分爲大小相等的兩塊。當一塊內存塊用完了,先將還存活的對象拷貝到另一塊中。然後清理已經使用過空間。這樣每次都是對一半的空間進行清理的。
 c 標記-整理算法
   主要用於清理老年代的內存空間的。其中標記過程和標記-清除是一致的,但是後面不是清理而是讓存活的對象都移到一端。然後直接清除端邊界以外的內存。
4 堆內存新生代的分區
   hotspot是將堆內存中存放新生代的內存分成一塊較大的Eden空間和兩塊較小的Survivor空間。當垃圾回收時,現將Eden和Survivor空間A中存活的對象複製到另一塊空閒的Survivor空間B中。最後清理Eden和Survivor空間A。Hotspot默認的Eden和Survivor的容量比是8:1。也就是新生代佔整個新生代容量的90%(80%+10%),只有10%的內存暫時沒有使用。當我們清理這90%的新生代空間時。如果Survivor空間無法放置存活的對象的時候我們只能將存活的對象通過擔保機制進入老年代。
  jvm的垃圾收集是按分代思想的。在hotspot中,會將java堆分成新生代和老年代。


5、Hotspot的算法實現
  a  jvm會使用一組稱爲OopMap的數據結構來獲取哪些地方存放着對象的引用。找到了引用就可以知道GC Roots節點的位置。
  b  找到合適的安全點來讓程序暫停下來。因爲執行gc操作的時候其他的程序是必須要停下來的。主要有兩種方法:搶先式中斷(幾乎沒有被使用過)和主動式中斷。
  c 如果遇到程序在當前的線程不執行,即當前線程是無法響應JVM的中斷請求。這個時候需要一個安全區域。當程序在安全區域的時候只要有gc操作就立馬執行。




6、垃圾收集器的種類
   java一共有7中不同的分代收集器。其中部分的收集器是可以配合使用的。其中,新生代採取複製算法暫停所有的用戶線程。老年代採取標記-整理算法暫停所有的用戶線程。
   a Serial收集器
     單線程的收集器。Serial收集器對於運行在Client模式下的虛擬機是很好的選擇。主要是收集新生代的垃圾。
   b ParNew收集器
    Serial收集器的多線程版。功能和Serial收集器一致。但它是運行在Server模式下的虛擬機首選的新生代收集器。
   c Parallel Scavenge收集器
  與ParNew收集器幾乎一樣。但在可控制gc的暫停時間的時候是利用吞吐量來控制的。
   d Serial Old 收集器
   Serial收集器收集老年代的版本。採用的是標記-整理的算法。
   e Parallel Old收集器
   Parallel Scavenge收集器的老年代版本。
   f CMS收集器
   是一種以獲取最短回收停頓時間爲目標的收集器。是基於標記-清除算法實現的。CMS收集器的優點:併發收集、低停頓
   g G1收集器
   面向服務端應用的垃圾收集器。它的運作可以分爲:初始標記、併發標記、最終標記、篩選迴歸。
  初始標記只是標記一下GC Roots能直接關聯的對象。這個階段需要停頓線程但耗時較短。 
  併發標記是從GC Roots開始對堆中對象進行可達性分析,找出存活的對象。這個階段耗時較長,但與用戶線程併發執行。
  最終標記是GC Roots節點對象不可達的對象的引用。
  最後在進行篩選回放,因爲還是有對象會再次被利用上的。執行完這一步就可以進行垃圾收集了。


7、內存分配和回收的策略
   a 對象優先在Eden空間分配
   b 大對象直接進入老年代
   c 長期存活的對象進入老年代
   d 要記住老年代會爲新生代進行空間分配的擔保
   e 新生代GC回收的速度快 ,老年代GC回收速度慢。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章