參考網址:http://www.blogjava.net/ldwblog/archive/2013/07/24/401919.html
? ? ? ? ? ? ? ? ? http://blog.csdn.net/a1134075691/article/details/51706275
? ? ? ? ? ? ? ? ? http://blog.csdn.net/a1134075691/article/details/51706275
GC的優化配置
配置描述
-Xms初始化堆內存大小
-Xmx堆內存最大值
-Xmn新生代大小
-XX:PermSize初始化永久代大小
-XX:MaxPermSize永久代最大容量
JVM的堆是Java對象的活動空間,程序中的類的對象從中分配空間,其存儲著正在運行著的應用程序用到的所有對象。這些對象的建立方式就是那些new一類的操作,當對象無用后,是GC來負責這個無用的對象(地球人都知道)。
被回收的目標是找到所有的GC的根結點(GC Root), 將他們放到隊列里,然后依次遞歸地遍歷所有的根結點以及引用的所有子節點和子子節點,將所有被遍歷到的結點標記成live。弱引用不會被考慮在內。
1、虛擬機(JVM)棧中的引用的對象,引用是在棧幀中的本地變量表中的,真正的對象在堆中
2、方法區中的類靜態屬性引用的對象
3、方法區中的常量引用的對象(主要指聲明為final的常量值)
4、本地方法棧中JNI的引用的對象
JVM堆
(1) 新域:存儲所有新成生的對象
(2) 舊域:新域中的對象,經過了一定次數的GC循環后,被移入舊域
(3)永久域:存儲類和方法對象,從配置的角度看,這個域是獨立的,不包括在JVM堆內。默認為4M。
新域會被分為3個部分:
1.第一個部分叫Eden。
2.另兩個部分稱為輔助生存空間,我這里一個稱為From 幸存區(survivor)”和”To 幸存區(survivor)”。
按照規定,新對象會首先分配在 Eden 中(如果新對象過大,會直接分配在老年代中)。在GC中,Eden 中的對象會被移動到survivor中,直至對象滿足一定的年紀(定義為熬過GC的次數),會被移動到老年代。
虛擬機給每個對象定義了一個對象年齡(Age)計數器。如果對象在 Eden 出生并經過第一次 Minor GC 后仍然存活,并且能被 Survivor 容納的話,將被移動到 Survivor 空間中,并將對象年齡設為 1。對象在 Survivor 區中每熬過一次 Minor GC,年齡就增加 1 歲,當它的年齡增加到一定程度(默認為 15 歲)時,就會被晉升到老年代中。對象晉升老年代的年齡閾值,可以通過參數 -XX:MaxTenuringThreshold 來設置。
小于時被HandlePromotionFailure參數強制full gc;gc與非gc時間耗時超過了GCTimeRatio的限制引發OOM,調優諸如通過NewRatio控制新生代老年代比例,通過MaxTenuringThreshold控制進入老年前生存次數等……
新生代通常存活時間較短,因此基于Copying算法來進行回收,所謂Copying算法就是掃描出存活的對象,并復制到一塊新的完全未使用的空間中,對應于新生代,就是在Eden和FromSpace或ToSpace之間copy。新生代采用空閑指針的方式來控制GC觸發,指針保持最后一個分配的對象在新生代區間的位置,當有新的對象要分配內存時,用于檢查空間是否足夠,不夠就觸發GC。當連續分配對象時,對象會逐漸從eden到survivor,最后到舊生代,
舊生代與新生代不同,對象存活的時間比較長,比較穩定,因此采用標記(Mark)算法來進行回收,所謂標記就是掃描出存活的對象,然后再進行回收未被標記的對象,回收后對用空出的空間要么進行合并,要么標記出來便于下次進行分配,總之就是要減少內存碎片帶來的效率損耗。在執行機制上JVM提供了串行GC(SerialMSC)、并行GC(parallelMSC)和并發GC(CMS),具體算法細節還有待進一步深入研究。
HandlePromotionFailure ?關閉新生代收集擔保。
什么是新生代收集擔保?
在一次理想化的minor gc中,Eden和First Survivor中的活躍對象會被復制到Second Survivor。
然而,Second Survivor不一定能容納下所有從E和F區copy過來的活躍對象。
為了確保minor gc能夠順利完成,GC需要在年老代中額外保留一塊足以容納所有活躍對象的內存空間。
這個預留操作,就被稱之為新生代收集擔保(New Generation Guarantee)。如果預留操作無法完成時,仍會觸發major gc(full gc)。
為什么要關閉新生代收集擔保?
因為在年老代中預留的空間大小,是無法精確計算的。
為了確保極端情況的發生,GC參考了最壞情況下的新生代內存占用,即Eden+First Survivor。
這種策略無疑是在浪費年老代內存,從時序角度看,還會提前觸發Full GC。
為了避免如上情況的發生,JVM允許開發者手動關閉新生代收集擔保。
在開啟本選項后,minor gc將不再提供新生代收集擔保,而是在出現survior或年老代不夠用時,拋出promotion failed異常。?
垃圾收集器的種類有:Serial收集器,ParNew收集器,Parallel Scavenge收集器,Serial Old收集器,Parallel Old收集器,CMS收集器,G1收集器
1)Seria串行l收集器 ---client級別默認
單線程,工作時需要暫停用戶所有線程,可以和CMS配合工作
2)ParNew并發收集器
多線程,工作時需要用戶暫停所有線程,可以和CMS配合工作
3)Parallel Scavenge并行回收收集器 ---server級別默認采用
Parallel Scavenge收集器的主要目標是達到可控制吞吐量。吞吐量就是CPU運行用戶代碼的時間占CPU消耗時間的比值,即吞吐量=運行用戶代碼的時間/(運行用戶代碼的時間+垃圾收集的時間)。
-XX:MaxGCPauseMillis=M設置垃圾收集時間盡量在M毫秒完成,減低M值不一定可以提高吞吐量
-XX:GCTimeRatio=N用于設置允許最大的垃圾收集時間占總時間的1/(1+N),默認值為99
-XX:+UseParallelGC來強制指定,用-XX:ParallelGCThreads=4來指定線程數
4)Serial Old收集器
Serial Old是Seria老年代版本,可以與Parallel Scavenge收集器配合使用,或者作為CMS的后備預案
5)Parallel Old收集器
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,Parallel Scavenge收集器不可以與CMS收集器配合使用,如果使用Serial Old收集器,性能會被拖累,所以可以選擇Parallel Old收集器,ParallelOld 垃圾收集只對整個堆執行壓縮。
6)Concurrent Mark & Sweep ? CMS并發收集器
通過JVM參數 -XX:+UseConcMarkSweepGC 使用
-XX:ParallelCMSThreads= 并發標記掃描垃圾回收器 =為使用的線程數量
第一步初始化標記(initial mark) 比較簡單。這一步驟只是查找那些距離類加載器最近的幸存對象。因此,停頓的時間非常短暫。在之后的并行標記(concurrent mark)步驟,所有被幸存對象引用的對象會被確認是否已經被追蹤和校驗。這一步的不同之處在于,在標記的過程中,其他的線程依然在執行。在重新標記(remark)步驟,會再次檢查那些在并行標記步驟中增加或者刪除的與幸存對象引用的對象。最后,在并行交換(concurrent sweep)步驟,轉交垃圾回收過程處理。垃圾回收工作會在其他線程的執行過程中展開。一旦采取了這種GC類型,由GC導致的暫停時間會極其短暫。CMS GC也被稱為低延遲GC。它經常被用在那些對于響應時間要求十分苛刻的應用之上。
CMS并發收集器的優點:并發收集,低停頓
CMS收集器的缺點:CMS對CPU比較敏感,默認線程數為(CPU數量+3)/4,無法處理浮動垃圾(垃圾回收過程中用戶線程沒有停止導致),標記清楚算法會產生空間碎片,它會比其他GC類型占用更多的內存和CPU.
在使用這個GC類型之前你需要慎重考慮。如果因為內存碎片過多而導致壓縮任務不得不執行,那么stop-the-world的時間要比其他任何GC類型都長,你需要考慮壓縮任務的發生頻率以及執行時間。?
7)G1收集器
JDK7時使用。G1收集器采用標記整理算法,沒有碎片;可以精確控制停頓,指定M毫秒的時間片段內,消耗在垃圾搜集上的時間不能超過N毫秒;可以在不犧牲吞吐量的情況下完成低停頓的垃圾回收,原理:把堆分成多個大小固定的獨立區域,并跟蹤垃圾的堆積程度,在后臺維護一個優先級列表。?
通過JVM參數–XX:+UseG1GC使用G1垃圾回收器
在使用G1垃圾回收器的時候,通過 JVM參數-XX:+UseStringDeduplication。 我們可以通過刪除重復的字符串,只保留一個char[]來優化堆內存。這個選擇在Java 8 u 20被引入。
階段說明
(1) 初始標記 (Initial Mark)(Stop the World Event,所有應用線程暫停) 在老年代(old generation)中的對象, 如果從年輕代(young generation)中能訪問到, 則被 “標記,marked” 為可達的(reachable).對象在舊一代“標志”可以包括這些對象可能可以從年輕一代。暫停時間一般持續時間較短,相對小的收集暫停時間.
(2) 并發標記 (Concurrent Marking)在Java應用程序線程運行的同時遍歷老年代(tenured generation)的可達對象圖。掃描從被標記的對象開始,直到遍歷完從root可達的所有對象. 調整器(mutators)在并發階段的2、3、5階段執行,在這些階段中新分配的所有對象(包括被提升的對象)都立刻標記為存活狀態.
(3) 再次標記(Remark)(Stop the World Event, 所有應用線程暫停) 查找在并發標記階段漏過的對象,這些對象是在并發收集器完成對象跟蹤之后由應用線程更新的.
(4) 并發清理(Concurrent Sweep)回收在標記階段(marking phases)確定為不可及的對象. 死對象的回收將此對象占用的空間增加到一個空閑列表(free list),供以后的分配使用。死對象的合并可能在此時發生. 請注意,存活的對象并沒有被移動.
(5) 重置(Resetting)清理數據結構,為下一個并發收集做準備.
垃圾收集器采用的算法:
1)標記清除算法
原理:對于“活”的對象,一定可以追溯到其存活在堆棧、靜態存儲區之中的引用。這個引用鏈條可能會穿過數個對象層次,算法基于有向圖,采用深度優先搜索。
第一階段:從GC roots開始遍歷所有的引用,對有活的對象進行標記。
第二階段:對堆進行遍歷,把未標記的對象進行清除。
優點:解決了循環引用的問題。
缺點:
(1)暫停整個應用;
(2)會產生內存碎片。
(3)不管你這個對象是不是可達的,即是不是垃圾,都要在清楚階段被檢查一遍,非常耗時.
2)復制算法
原理:把內存空間劃分為2個相等的區域,每次只使用一個區域。垃圾回收時,遍歷當前使用區域,把正在使用的對象復制到另外一個區域。
優點:不會出現碎片問題。
缺點:
(1)暫停整個應用。
(2)需要2倍的內存空間。
3)標記整理算法
4)分代搜集
把堆分成新生代以及老年代,新生代使用復制算法,老年代使用標記整理算法
java虛擬機沒有使用引用計數算法,而是使用根搜索算法來查找不可用對象。
引用計數算法:當一個對象被引用,引用計數器加1,當引用失效的時候,引用計數器減1,當引用計數器為0的時候,被認為是可收回對象。
根搜索算法:通過一系列的”GC ROOTS“對象作為起始點,從這些點向下搜索,不能喝GC ROOTS聯通的對象便是可回收對象
java中可以作為“GC ROOTS”的對象包括:虛擬機棧(棧幀中的本地變量表)中的引用對象,方法區中的類靜態屬性引用的對象,方法區中的常量引用對象,本地方法棧中JNI(Native方法)的引用對象。
引用的類型:
強引用:只要強引用還存在,GC永遠不會回收內存
軟引用:對于軟引用關聯著的對象,當要發生內存溢出異常之前,把這些對象列入回收范圍并進行第二次回收
弱引用:對于弱引用關聯著的對象,能下一次GC工作的時候,不管內存是否足夠,都會被回收
虛引用:虛引用不會影響關聯著的對象的生存時間,也無法通過虛引用來獲取對象的實例,虛引用的唯一目的就是這個對象被GC回收時收到一個系統通知
垃圾收集器的執行邏輯:
1)用根搜索算法進行第一次標記
2)對標記的對象進行一次篩選,如果對象覆蓋了finalize方法,并且沒有執行過,那么有可能在這個finalize方法中再次使用這個對象,這樣的話就把該對象從待刪除的對象隊列中刪除。finalize方法只能執行一次,保證對象循環引用的時候不可能永遠存在。
3)java虛擬機創建一個低優先級的Finalize線程去執行對象覆蓋的finalize方法,垃圾收集器對待刪除隊列再次進行標記
4)垃圾收集器把兩次標記的對象回收了
===========================================================================
并行和并發的區別
這兩個名詞都是并發編程中的概念,在談論垃圾收集器的上下文語境中,可以這么理解這兩個名詞:
1、并行Parallel
多條垃圾收集線程并行工作,但此時用戶線程仍然處于等待狀態
2、并發Concurrent
指用戶線程與垃圾收集線程同時執行(但并不一定是并行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集程序運行于另一個CPU上
Minor GC和Full GC的區別
1、新生代GC(Minor GC)
指發生在新生代的垃圾收集動作,因為大多數Java對象存活率都不高,所以Minor GC非常頻繁,一般回收速度也比較快
2、老年代GC(Major GC/Full GC)
指發生在老年代的垃圾收集動作,出現了Major GC,經常會伴隨至少一次的Minor GC(但并不是絕對的)。Major GC的速度一般要比Minor GC慢上10倍以上