來自:Giraffe's Homehtml
連接:http://yemengying.com/2016/05/13/jvm-GC/(點擊尾部閱讀原文前往)java
原文:http://javarevisited.blogspot.com/2011/04/garbage-collection-in-java.html程序員
翻譯一篇關於垃圾回收(如下簡稱GC)機制的博客內容包括:Java中GC是如何工做的,常見的GC算法(好比:標記清除),Java中不一樣的垃圾收集器(好比:serial)算法
關鍵字約定
-
Young generation >新生代數組
-
Tenured / Old Generation >老年代多線程
-
Perm Area >永久代併發
重要的東東
-
在Java中,對象實例都是在堆上建立一些類信息,常量,靜態變量等存儲在方法區堆和方法區都是線程共享的jvm
-
GC機制是由JVM提供,用來清理須要清除的對象,回收堆內存性能
-
GC機制將Java程序員從內存管理中解放了出來,能夠更關注於業務邏輯線程
-
在Java中,GC是由一個被稱爲垃圾回收器的守護進程執行的
-
在從內存回收一個對象以前會調用對象的finalize()方法
-
做爲一個Java開發者不能強制JVM執行GC;GC的觸發由JVM依據堆內存的大小來決定
-
System.gc()和Runtime.gc()會向JVM發送執行GC的請求,可是JVM不保證必定會執行GC
-
若是堆沒有內存建立新的對象了,會拋出OutOfMemoryError
GC針對什麼對象?
瞭解GC機制的第一步就是理解什麼樣的對象會被回收當一個對象經過一系列根對象(好比:靜態屬性引用的常量)都不可達時就會被回收簡而言之,當一個對象的全部引用都爲null循環依賴不算作引用,若是對象A有一個指向對象B的引用,對象B也有一個指向對象A的引用,除此以外,它們沒有其餘引用,那麼對象A和對象B都須要被回收(以下圖,ObjA和ObjB須要被回收)
堆內存是如何劃分的?
Java中對象都在堆上建立爲了GC,堆內存分爲三個部分,也能夠說三代,分別稱爲新生代,老年代和永久代其中新生代又進一步分爲Eden區,Survivor 1區和Survivor 2區(以下圖)新建立的對象會分配在Eden區,在經歷一次Minor GC後會被移到Survivor 1區,再經歷一次Minor GC後會被移到Survivor 2區,直到升至老年代,須要注意的是,一些大對象(長字符串或數組)可能會直接存放到老年代
永久代有一些特殊,它用來存儲類的元信息對於GC是否發生在永久代有許多不一樣的見解,在我看來這取決於採用的JVM你們能夠經過建立大量的字符串來觀察是發生了GC仍是拋出了OutOfMemoryError
GC算法
1標記清除算法
分爲標記和清除兩個階段:首先標記出全部須要回收的對象,在標記完成後統一回收全部被標記的對象該算法的缺點是效率不高而且會產生不連續的內存碎片
2複製算法
把內存空間劃爲兩個區域,每次只使用其中一個區域垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另一個區域中次算法每次只處理正在使用中的對象,所以複製成本比較小,同時複製過去之後還能進行相應的內存整理,不會出現碎片問題優勢:實現簡單,運行高效缺點:會浪費必定的內存通常新生代採用這種算法
image
3標記整理算法
標記階段與標記清除算法同樣但後續並非直接對可回收的對象進行清理,而是讓全部存活對象都想一端移動,而後清理優勢是不會形成內存碎片
Java中垃圾回收器的類型
Java提供多種類型的垃圾回收器JVM中的垃圾收集通常都採用分代收集,不一樣的堆內存區域採用不一樣的收集算法,主要目的就是爲了增長吞吐量或下降停頓時間
-
Serial收集器:新生代收集器,使用複製算法,使用一個線程進行GC,串行,其它工做線程暫停
-
ParNew收集器:新生代收集器,使用複製算法,Serial收集器的多線程版,用多個線程進行GC,並行,其它工做線程暫停使用-XX:+UseParNewGC開關來控制使用ParNew+Serial Old收集器組合收集內存;使用-XX:ParallelGCThreads來設置執行內存回收的線程數
-
Parallel Scavenge 收集器:吞吐量優先的垃圾回收器,做用在新生代,使用複製算法,關注CPU吞吐量,即運行用戶代碼的時間/總時間使用-XX:+UseParallelGC開關控制使用Parallel Scavenge+Serial Old收集器組合回收垃圾
-
Serial Old收集器:老年代收集器,單線程收集器,串行,使用標記整理算法,使用單線程進行GC,其它工做線程暫停
-
Parallel Old收集器:吞吐量優先的垃圾回收器,做用在老年代,多線程,並行,多線程機制與Parallel Scavenge差不錯,使用標記整理算法,在Parallel Old執行時,仍然須要暫停其它線程
-
CMS(Concurrent Mark Sweep)收集器:老年代收集器,致力於獲取最短回收停頓時間(即縮短垃圾回收的時間),使用標記清除算法,多線程,優勢是併發收集(用戶線程能夠和GC線程同時工做),停頓小使用-XX:+UseConcMarkSweepGC進行ParNew+CMS+Serial Old進行內存回收,優先使用ParNew+CMS(緣由見Full GC和併發垃圾回收一節),當用戶線程內存不足時,採用備用方案Serial Old收集
能夠看Java Performance一書來獲取更多關於GC調優的信息
與GC有關的JVM參數
作GC調優須要大量的實踐,耐心和對項目的分析我曾經參與太高容量,低延遲的電商系統,在開發中咱們須要經過分析形成Full GC的緣由來提升系統性能,在這個過程當中我發現作GC的調優很大程度上依賴於對系統的分析,系統擁有怎樣的對象以及他們的平均生命週期
舉個例子,若是一個應用大可能是短生命週期的對象,那麼應該確保Eden區足夠大,這樣能夠減小Minor GC的次數能夠經過-XX:NewRatio來控制新生代和老年代的比例,好比-XX:NewRatio=3表明新生代和老年代的比例爲1:3須要注意的是,擴大新生代的大小會減小老年代的大小,這會致使Major GC執行的更頻繁,而Major GC可能會形成用戶線程的停頓從而下降系統吞吐量JVM中能夠用NewSize和MaxNewSize參數來指定新生代內存最小和最大值,若是兩個參數值同樣,那麼就至關於固定了新生代的大小
我的建議,在作GC調優以前最好深刻理解Java中GC機制,推薦閱讀Sun Microsystems提供的有關GC的文檔這個連接可能會對理解GC機制提供一些幫助下面的圖列出了各個區可用的一些JVM參數
jvm參數
Full GC和併發垃圾回收
併發垃圾回收器的內存回收過程是與用戶線程一塊兒併發執行的一般狀況下,併發垃圾回收器能夠在用戶線程運行的狀況下完成大部分的回收工做,因此應用停頓時間很短
但因爲併發垃圾回收時用戶線程還在運行,因此會有新的垃圾不斷產生做爲擔保,若是在老年代內存都被佔用以前,若是併發垃圾回收器還沒結束工做,那麼應用會暫停,在全部用戶線程中止的狀況下完成回收這種狀況稱做Full GC,這意味着須要調整有關併發回收的參數了
因爲Full GC很影響應用的性能,要儘可能避免或減小特別是若是對於高容量低延遲的電商系統,要儘可能避免在交易時間段發生Full GC
總結
-
爲了分代垃圾回收,Java堆內存分爲3代:新生代,老年代和永久代
-
新的對象實例會優先分配在新生代,在經歷幾回Minor GC後(默認15次),還存活的會被移至老年代(某些大對象會直接在老年代分配)
-
永久代是否執行GC,取決於採用的JVM
-
Minor GC發生在新生代,當Eden區沒有足夠空間時,會發起一次Minor GC,將Eden區中的存活對象移至Survivor區Major GC發生在老年代,當升到老年代的對象大於老年代剩餘空間時會發生Major GC
-
發生Major GC時用戶線程會暫停,會下降系統性能和吞吐量
-
JVM的參數-Xmx和-Xms用來設置Java堆內存的初始大小和最大值依據我的經驗這個值的比例最好是1:1或者1:1.5好比,你能夠將-Xmx和-Xms都設爲1GB,或者-Xmx和-Xms設爲1.2GB和1.8GB
-
Java中不能手動觸發GC,但能夠用不一樣的引用類來輔助垃圾回收器工做(好比:弱引用或軟引用)
以上就是關於Java中GC的一些內容經過這篇博客,咱們能夠知道堆內存是如何劃分的;一個對象在沒有任何強引用指向他或該對象經過根節點不可達時須要被垃圾回收器回收;當垃圾收集器意識到須要進行GC時會觸發Minor GC或Major GC,是自動的,沒法強制執行
參考文檔(康桑阿米達~)
-
http://icyfenix.iteye.com/blog/715****01
-
http://www.cnblogs.com/zhguang/p/325****67.html
-
深刻理解java虛擬機