說明本文是閱讀《Java性能權(quán)威指南》的第五章節(jié)梳理內(nèi)容,不是本人原創(chuàng),覺得寫的非常好,特意總結(jié)下,所以有了下文。
1.GC作用
主要做三件事情:
-
查找不可用的對象。
不能采用簡單的引用計數(shù)功能,因為有類似于雙向鏈表的將無法清除。 -
回收垃圾對象占用內(nèi)存
不同的垃圾回收器,采用不同的算法,采用單線程或多線程來回收垃圾對
象。 -
內(nèi)存碎片的整理
將回收后的對象進(jìn)行壓縮形成大的空閑區(qū)域。

2.垃圾回收共有特性
均采用分代回收形式
分代可以將堆空間劃分成不同的代,減少每個代的大小,有利于掃描垃圾對象。所有的GC回收算法在新生代都會暫停所有應(yīng)用程序線程
新生代直接涉及到的是應(yīng)用程序?qū)ο蟮姆峙?,在分配的時候,會更改對象的空間。** 新生代的垃圾回收稱為Minor GC**
新生代初始分配空間為Enden,要么對象移到老年代,要么移到Survior空間。** 老年代空間即將占滿時候回收,采用不同垃圾回收算法**
簡單垃圾回收算法: 暫停應(yīng)用程序線程,直接進(jìn)行Full GC。
CMS/G1: 盡量減少停頓,會占用更多CPU稱為Concurrent收集器。
3. GC垃圾回收算法選擇
1.使用內(nèi)存小于100M
使用Serial 垃圾收集器。
2. Concurrent or Throughput
單核心CPU或者cpu很忙:利用Throughput,處理應(yīng)用程序的批處理任務(wù),通??梢垣@得更好的性能,因為在cpu很忙的時候,CMS多線程和應(yīng)用程序爭奪資源會造成系統(tǒng)運行更慢。
多核CPU或CPU資源不忙:
采用Concurrent收集器可以獲得更好的性能。
3. CMS 和 Throughput 的CPU利用率特點:
Throughput: 在進(jìn)行Full GC或Minor GC 都會占用100%cpu;在90%和99%的響應(yīng)時間上更有優(yōu)勢,如果超負(fù)荷運行大量Full GC,則切換到Concurrent性能更好。
CMS: 在后臺垃圾回收線程和應(yīng)用程序線程同時運行,CPU利用率更平滑,偶爾達(dá)到100%。在CPU資源不夠時候,可能會退化到Serial方式,CPU占不滿,性能比Throughput 差。
4. CMS VS G1
基本原則:堆內(nèi)存小于4G情況下,建議用CMS ,性能更好;大型堆采用G1更好。
CMS
CMS后臺線程需要掃描整個老年代內(nèi)存,掃描時間與堆大小關(guān)系密切,如果在堆未填滿之前,CMS后臺線程停止掃描,直接回收對象,則發(fā)生并發(fā)失效。一旦發(fā)生了Concurrent Mode Failure,則處理Full GC的只有一個線程,性能損耗嚴(yán)重,CMS并發(fā)模式失效同時也會受到程序內(nèi)存分配的影響。另外堆的碎片化也會導(dǎo)致Full GC。
G1
將堆分為多個區(qū)域,更利于使用多線程分擔(dān)掃描老年代,如果后臺線程跟不上處理速度也會發(fā)生并發(fā)模式失效的問題;也會發(fā)生堆的內(nèi)存碎片化,不過比CMS要好。
此外說明:Throughput 比CMS、G1 更老,所以經(jīng)過長期驗證,穩(wěn)定性,可調(diào)整參數(shù)更多,相反G1新的垃圾回收器,容易遇到極端情況多。
5. GC調(diào)優(yōu)基礎(chǔ)
內(nèi)存大小調(diào)節(jié)
堆大小設(shè)置:
設(shè)置過?。簩?dǎo)致一直在GC,程序執(zhí)行速度慢。
設(shè)置過大:
1.導(dǎo)致垃圾回收時間長,停頓時間短,但是持續(xù)時間會讓程序整體性能變慢。
2.JVM虛擬內(nèi)存如果占用了系統(tǒng)的虛擬內(nèi)存,F(xiàn)ull GC性能將會非常糟糕。
堆設(shè)置的首要原則
永遠(yuǎn)不要將堆的總大小設(shè)置超過機(jī)器的物理內(nèi)存,特別是有多個JVM在跑的時候,通常情況對于普通的操作系統(tǒng)需要預(yù)留1G的內(nèi)存給操作系統(tǒng)。
堆大小控制參數(shù)
-XmsN 設(shè)置初始值
-XmxN 設(shè)置最大值
默認(rèn)值取決于操作系統(tǒng)、系統(tǒng)內(nèi)存大小、JVM和命令行標(biāo)志,堆大小調(diào)節(jié)是JVM自適應(yīng)調(diào)節(jié)核心。
堆默認(rèn)值

經(jīng)驗法則:
一次Full GC后盡量保持70%內(nèi)存可用。
設(shè)置方法:在穩(wěn)定運行程序的時候通過Jconsole連接程序進(jìn)行Full GC觀察有多少內(nèi)存被占用。
初始值和最大值設(shè)置一樣可以稍微提高GC運行效率,調(diào)整盡量調(diào)整算法。
代空間調(diào)整
需要調(diào)整的原因:給新生代分配的比較大,垃圾收集的頻率降低,從新生代晉級的也比較少,但是相應(yīng)的老年代的相對較小,比較容易被填滿,從而造成Full GC,所以需要找個平衡點。
所有的GC算法都采用同一套標(biāo)志來設(shè)置代的大?。?br>
-XX:NewRatio=N
設(shè)置新生代與老年代的空間占比率
-XX:NewSize= N
設(shè)置新生代的空間大小
-XX:MaxNewSize=N
設(shè)置新生代空間的最大大小
-XmnN
將新生代的NewSize和MaxNewSize設(shè)置為同一個值。
最初新生代空間由NewRadtio決定,默認(rèn)設(shè)置為2.
新生代空間= 初始堆大小/(1+NewRadio) 即新生代空間為初始堆的33%。
NewSize 比NewRadio優(yōu)先級更高。
建議: 如果堆大小固定,則新生代可以設(shè)置為固定值;否則可以設(shè)置為按照比例。
整個堆空間的劃分是由新生代的大小來決定的。
永生代/元空間調(diào)整
- Java7保存的是類元數(shù)據(jù)信息,稱為永久代,還保存一些類無關(guān)的雜項信息。
- Java8改名永生代為元空間只對編譯器和JVM運行時候有用。
永久代/元空間默認(rèn)大?。?/li>

對永久代而言:
可以通過-XX:PermSize=N / -XX:MaxPermSize=N類設(shè)置
對于元空間而言:
可以通過-XX:MetaspaceSize=N/ -XX:MaxMetaspaceSize= N 來設(shè)置
增加啟動速度可以通過增加用具帶或元空間達(dá)到,特別是才啟動有大量Full GC,
通常設(shè)置為128MB/192MB
同樣會被垃圾回收,不是永久存在。
比如類加載器,通過jmap –permstat 或-clstats來查看輸出類加載器信息。
6. GC的并發(fā)控制
控制參數(shù): -XX:ParallelGCThreads=N
影響下面的線程數(shù)目:
-XX:+UseParallelGC 收集新生代空間
-XX:+UseParallelOldGC 收集老生代空間
-XX:+UseNewParNewGC: 收集新生代空間
-XX:+UseG1GC 收集新生代空間
CMS收集器的STW階段(非Full GC)
G1收集器的STW階段(非Full GC)
GC默認(rèn)線程數(shù):
1、在小于8個cpu的時候,一個cpu啟動一個線程。
2、一旦超過8個則:parallelGCThread= 8 + ((N-8)*5/8)
3、在8核或更少核心,JVM 100%占用cpu,在更多核心上,運行垃圾回收器會更多占用cpu,例如一個16核心cpu,如果運行4個JVM,則默認(rèn)一個13個線程,則嚴(yán)重影響程序性能,建議每個JVM設(shè)置4個垃圾回收線程比較合理。
7.自適應(yīng)調(diào)節(jié)
JVM自動運行的時候,根據(jù)默認(rèn)的指標(biāo)自動調(diào)整代的大小盡量的滿足目標(biāo)。
好處:
1、以為小型程序不需要為指定過大的堆而擔(dān)心。
2、不需要擔(dān)心堆大小,JVM自動根據(jù)優(yōu)化目標(biāo)調(diào)整堆的大小。
3、缺點:調(diào)整需要時間開銷,如果已經(jīng)做了優(yōu)化,可以考慮關(guān)閉自適應(yīng)調(diào)整。
通過-XX:+UseAdaptiveSizePolicy 在全局范圍關(guān)閉自適應(yīng)調(diào)整功能。
如果堆最大和最小設(shè)置相同,新生代的最大最小設(shè)置相同,則將會自動關(guān)閉自適應(yīng)調(diào)整。
4、如果設(shè)置-XX:+PrintAdaptiveSizePolicy標(biāo)志,則垃圾回收日志打印不同代的調(diào)整細(xì)節(jié)
8.垃圾回收工具
1、判斷垃圾回收性能好壞需要日志
-verbose:gc 或-XX:+PrintGC 這兩個標(biāo)示中的任意一個可以創(chuàng)建基本的GC日志
2、詳細(xì)垃圾回收日志
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps 或-XX:+PrintGCDateStamps 便于查看幾次垃圾回收操作之間的時間。
前者時間戳是基于0(JVM啟動時間)的;后者基于真正日期,需要耗費更多性能。
3、垃圾回收結(jié)果輸出到日志:
-Xloggc:filename
-XX:+UseGCLogfileRotation –XX:NumberOfGCLogFileRotation=N –XX:GCLogfileSize=N
來設(shè)置gc日志輪詢打印和輪詢打印的日志文件數(shù)、日志文件大小。
4、利用工具查看日志
GC Histogram 讀取日志生成圖形表格
Jconsole 內(nèi)存板可以查看堆的使用情況。
Jstat –gcutil 能夠打印各個區(qū)的占用百分比和垃圾回收情況,
Jstat –gcutil process_id 1000 【每隔1s打印一次情況】