阿里終面:每天100w次登陸請求,8G內(nèi)存該如何設(shè)置JVM參數(shù)?
下面以面試題的形式給大家梳理出來,做到一箭雙雕:
- 既供大家實操參考
- 又供大家面試參考
大家要學(xué)習(xí)的,除了 JVM 配置方案 之外,是其 分析問題的思路、思考問題的視角。這些思路和視角,能幫助大家走更遠(yuǎn)、更遠(yuǎn)。
接下來,進入正題。
每天100w次登陸請求, 8G 內(nèi)存該如何設(shè)置JVM參數(shù)?
每天100w次登陸請求, 8G 內(nèi)存該如何設(shè)置JVM參數(shù),大概可以分為以下8個步驟。
Step1:新系統(tǒng)上線如何規(guī)劃容量?
1.套路總結(jié)
任何新的業(yè)務(wù)系統(tǒng)在上線以前都需要去估算服務(wù)器配置和JVM的內(nèi)存參數(shù),這個容量與資源規(guī)劃并不僅僅是系統(tǒng)架構(gòu)師的隨意估算的,需要根據(jù)系統(tǒng)所在業(yè)務(wù)場景去估算,推斷出來一個系統(tǒng)運行模型,評估JVM性能和GC頻率等等指標(biāo)。以下是我結(jié)合大牛經(jīng)驗以及自身實踐來總結(jié)出來的一個建模步驟:
- 計算業(yè)務(wù)系統(tǒng)每秒鐘創(chuàng)建的對象會佔用多大的內(nèi)存空間,然后計算集群下的每個系統(tǒng)每秒的內(nèi)存佔用空間(對象創(chuàng)建速度)
- 設(shè)置一個機器配置,估算新生代的空間,比較不同新生代大小之下,多久觸發(fā)一次MinorGC。
- 為了避免頻繁GC,就可以重新估算需要多少機器配置,部署多少臺機器,給JVM多大內(nèi)存空間,新生代多大空間。
- 根據(jù)這套配置,基本可以推算出整個系統(tǒng)的運行模型,每秒創(chuàng)建多少對象,1s以后成為垃圾,系統(tǒng)運行多久新生代會觸發(fā)一次GC,頻率多高。
2.套路實戰(zhàn)——以登錄系統(tǒng)為例
有些同學(xué)看到這些步驟還是發(fā)憷,說的好像是那么回事,一到實際項目中到底怎麼做我還是不知道!
光說不練假把式,以登錄系統(tǒng)為例模擬一下推演過程:
- 假設(shè)每天100w次登陸請求,登陸峰值在早上,預(yù)估峰值時期每秒100次登陸請求。
- 假設(shè)部署3臺服務(wù)器,每臺機器每秒處理30次登陸請求,假設(shè)一個登陸請求需要處理1秒鐘,JVM新生代里每秒就要生成30個登陸對象,1s之后請求完畢這些對象成為了垃圾。
- 一個登陸請求對象假設(shè)20個字段,一個對象估算500字節(jié),30個登陸佔用大約15kb,考慮到RPC和DB操作,網(wǎng)絡(luò)通信、寫庫、寫緩存一頓操作下來,可以擴大到20-50倍,大約1s產(chǎn)生幾百k-1M數(shù)據(jù)。
- 假設(shè)2C4G機器部署,分配2G堆內(nèi)存,新生代則只有幾百M,按照1s1M的垃圾產(chǎn)生速度,幾百秒就會觸發(fā)一次MinorGC了。
- 假設(shè)4C8G機器部署,分配4G堆內(nèi)存,新生代分配2G,如此需要幾個小時才會觸發(fā)一次MinorGC。
所以,可以粗略的推斷出來一個每天100w次請求的登錄系統(tǒng),按照4C8G的3實例集群配置,分配4G堆內(nèi)存、2G新生代的JVM,可以保障系統(tǒng)的一個正常負(fù)載。
基本上把一個新系統(tǒng)的資源評估了出來,所以搭建新系統(tǒng)要每個實例需要多少容量多少配置,集群配置多少個實例等等這些,并不是拍拍腦袋和胸脯就可以決定的下來的。
Step2:該如何進行垃圾回收器的選擇?
吞吐量還是響應(yīng)時間
首先引入兩個概念:吞吐量和低延遲
吞吐量 = CPU在用戶應(yīng)用程序運行的時間 / (CPU在用戶應(yīng)用程序運行的時間 + CPU垃圾回收的時間)
響應(yīng)時間 = 平均每次的GC的耗時
通常,吞吐優(yōu)先還是響應(yīng)優(yōu)先這個在JVM中是一個兩難之選。
堆內(nèi)存增大,gc一次能處理的數(shù)量變大,吞吐量大;但是gc一次的時間會變長,導(dǎo)致后面排隊的線程等待時間變長;相反,如果堆內(nèi)存小,gc一次時間短,排隊等待的線程等待時間變短,延遲減少,但一次請求的數(shù)量變?。ú⒉唤^對符合)。
無法同時兼顧,是吞吐優(yōu)先還是響應(yīng)優(yōu)先,這是一個需要權(quán)衡的問題。
垃圾回收器設(shè)計上的考量
- JVM在GC時不允許一邊垃圾回收,一邊還創(chuàng)建新對象(就像不能一邊打掃衛(wèi)生,還在一邊扔垃圾)。
- JVM需要一段Stop the world的暫停時間,而STW會造成系統(tǒng)短暫停頓不能處理任何請求;
- 新生代收集頻率高,性能優(yōu)先,常用復(fù)制算法;老年代頻次低,空間敏感,避免復(fù)制方式。
- 所有垃圾回收器的涉及目標(biāo)都是要讓GC頻率更少,時間更短,減少GC對系統(tǒng)影響!
CMS和G1
目前主流的垃圾回收器配置是新生代采用ParNew,老年代采用CMS組合的方式,或者是完全采用G1回收器,
從未來的趨勢來看,G1是官方維護和更為推崇的垃圾回收器。
業(yè)務(wù)系統(tǒng):
- 延遲敏感的推薦CMS;
- 大內(nèi)存服務(wù),要求高吞吐的,采用G1回收器!
CMS垃圾回收器的工作機制
CMS主要是針對老年代的回收器,老年代是標(biāo)記-清除,默認(rèn)會在一次FullGC算法后做整理算法,清理內(nèi)存碎片。
CMS GC | 描述 | Stop the world | 速度 |
1.開始標(biāo)記 | 初始標(biāo)記僅標(biāo)記GCRoots能直接關(guān)聯(lián)到的對象,速度很快 | Yes | 很快 |
2.并發(fā)標(biāo)記 | 并發(fā)標(biāo)記階段就是進行GCRoots Tracing的過程 | No | 慢 |
3.重新標(biāo)記 | 重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄。 | Yes | 很快 |
4.垃圾回收 | 并發(fā)清理垃圾對象(標(biāo)記清除算法) | No | 慢 |
- 優(yōu)點:并發(fā)收集、主打“低延時” 。在最耗時的兩個階段都沒有發(fā)生STW,而需要STW的階段都以很快速度完成。
- 缺點:1、消耗CPU;2、浮動垃圾;3、內(nèi)存碎片
- 適用場景:重視服務(wù)器響應(yīng)速度,要求系統(tǒng)停頓時間最短。
總之:
業(yè)務(wù)系統(tǒng),延遲敏感的推薦CMS;
大內(nèi)存服務(wù),要求高吞吐的,采用G1回收器!
Step3:如何對各個分區(qū)的比例、大小進行規(guī)劃
一般的思路為:
首先,JVM最重要最核心的參數(shù)是去評估內(nèi)存和分配,第一步需要指定堆內(nèi)存的大小,這個是系統(tǒng)上線必須要做的,-Xms 初始堆大小,-Xmx 最大堆大小,后臺Java服務(wù)中一般都指定為系統(tǒng)內(nèi)存的一半,過大會佔用服務(wù)器的系統(tǒng)資源,過小則無法發(fā)揮JVM的最佳性能。
其次,需要指定-Xmn新生代的大小,這個參數(shù)非常關(guān)鍵,靈活度很大,雖然sun官方推薦為3/8大小,但是要根據(jù)業(yè)務(wù)場景來定,針對于無狀態(tài)或者輕狀態(tài)服務(wù)(現(xiàn)在最常見的業(yè)務(wù)系統(tǒng)如Web應(yīng)用)來說,一般新生代甚至可以給到堆內(nèi)存的3/4大小;關(guān)注公z號:碼猿技術(shù)專欄,回復(fù)關(guān)鍵詞:1111 獲取阿里內(nèi)部java性能調(diào)優(yōu)手冊!而對于有狀態(tài)服務(wù)(常見如IM服務(wù)、網(wǎng)關(guān)接入層等系統(tǒng))新生代可以按照默認(rèn)比例1/3來設(shè)置。服務(wù)有狀態(tài),則意味著會有更多的本地緩存和會話狀態(tài)信息常駐內(nèi)存,應(yīng)為要給老年代設(shè)置更大的空間來存放這些對象。
最后,是設(shè)置-Xss棧內(nèi)存大小,設(shè)置單個線程棧大小,默認(rèn)值和JDK版本、系統(tǒng)有關(guān),一般默認(rèn)512~1024kb。一個后臺服務(wù)如果常駐線程有幾百個,那麼棧內(nèi)存這邊也會佔用了幾百M的大小。
JVM參數(shù) | 描述 | 默認(rèn) | 推薦 |
-Xms | Java堆內(nèi)存的大小 | OS內(nèi)存64/1 | OS內(nèi)存一半 |
-Xmx | Java堆內(nèi)存的最大大小 | OS內(nèi)存4/1 | OS內(nèi)存一半 |
-Xmn | Java堆內(nèi)存中的新生代大小,扣除新生代剩下的就是老年代的內(nèi)存大小了 | 跌認(rèn)堆的1/3 | sun推薦3/8 |
-Xss | 每個線程的棧內(nèi)存大小 | 和idk有關(guān) | sun |
對于8G內(nèi)存,一般分配一半的最大內(nèi)存就可以了,因為機器本上還要占用一定內(nèi)存,一般是分配4G內(nèi)存給JVM,
引入性能壓測環(huán)節(jié),測試同學(xué)對登錄接口壓至1s內(nèi)60M的對象生成速度,采用ParNew+CMS的組合回收器,
正常的JVM參數(shù)配置如下:
這樣設(shè)置可能會由于動態(tài)對象年齡判斷原則導(dǎo)致頻繁full gc。為啥呢?
壓測過程中,短時間(比如20S后)Eden區(qū)就滿了,此時再運行的時候?qū)ο笠呀?jīng)無法分配,會觸發(fā)MinorGC,
假設(shè)在這次GC后S1裝入100M,馬上過20S又會觸發(fā)一次MinorGC,多出來的100M存活對象+S1區(qū)的100M已經(jīng)無法順利放入到S2區(qū),此時就會觸發(fā)JVM的動態(tài)年齡機制,將一批100M左右的對象推到老年代保存,持續(xù)運行一段時間,系統(tǒng)可能一個小時候內(nèi)就會觸發(fā)一次FullGC。
按照默認(rèn)8:1:1的比例來分配時, survivor區(qū)只有 1G的 10%左右,也就是幾十到100M,
如果 每次minor GC垃圾回收過后進入survivor對象很多,并且survivor對象大小很快超過 Survivor 的 50% , 那么會觸發(fā)動態(tài)年齡判定規(guī)則,讓部分對象進入老年代.
而一個GC過程中,可能部分WEB請求未處理完畢, 幾十兆對象,進入survivor的概率,是非常大的,甚至是一定會發(fā)生的.
如何解決這個問題呢?為了讓對象盡可能的在新生代的eden區(qū)和survivor區(qū), 盡可能的讓survivor區(qū)內(nèi)存多一點,達(dá)到200兆左右,
于是我們可以更新下JVM參數(shù)設(shè)置:
survivor達(dá)到200m,如果幾十兆對象到底survivor, survivor 也不一定超過 50%
這樣可以防止每次垃圾回收過后,survivor對象太早超過 50% ,
這樣就降低了因為對象動態(tài)年齡判斷原則導(dǎo)致的對象頻繁進入老年代的問題,
什么是JVM動態(tài)年齡判斷規(guī)則呢?
對象進入老年代的動態(tài)年齡判斷規(guī)則(動態(tài)晉升年齡計算閾值):Minor GC 時,Survivor 中年齡 1 到 N 的對象大小超過 Survivor 的 50% 時,則將大于等于年齡 N 的對象放入老年代。
核心的優(yōu)化策略是:是讓短期存活的對象盡量都留在survivor里,不要進入老年代,這樣在minor gc的時候這些對象都會被回收,不會進到老年代從而導(dǎo)致full gc。
應(yīng)該如何去評估新生代內(nèi)存和分配合適?
這里特別說一下,JVM最重要最核心的參數(shù)是去評估內(nèi)存和分配,
第一步需要指定堆內(nèi)存的大小,這個是系統(tǒng)上線必須要做的,-Xms 初始堆大小,-Xmx 最大堆大小,
后臺Java服務(wù)中一般都指定為系統(tǒng)內(nèi)存的一半,過大會佔用服務(wù)器的系統(tǒng)資源,過小則無法發(fā)揮JVM的最佳性能。
其次需要指定-Xmn新生代的大小,這個參數(shù)非常關(guān)鍵,靈活度很大,雖然sun官方推薦為3/8大小,但是要根據(jù)業(yè)務(wù)場景來定:
- 針對于無狀態(tài)或者輕狀態(tài)服務(wù)(現(xiàn)在最常見的業(yè)務(wù)系統(tǒng)如Web應(yīng)用)來說,一般新生代甚至可以給到堆內(nèi)存的3/4大??;
- 而對于有狀態(tài)服務(wù)(常見如IM服務(wù)、網(wǎng)關(guān)接入層等系統(tǒng))新生代可以按照默認(rèn)比例1/3來設(shè)置。
服務(wù)有狀態(tài),則意味著會有更多的本地緩存和會話狀態(tài)信息常駐內(nèi)存,應(yīng)為要給老年代設(shè)置更大的空間來存放這些對象。
step4:棧內(nèi)存大小多少比較合適?
-Xss棧內(nèi)存大小,設(shè)置單個線程棧大小,默認(rèn)值和JDK版本、系統(tǒng)有關(guān),一般默認(rèn)512~1024kb。一個后臺服務(wù)如果常駐線程有幾百個,那麼棧內(nèi)存這邊也會佔用了幾百M的大小。
step5:對象年齡應(yīng)該為多少才移動到老年代比較合適?
假設(shè)一次minor gc要間隔二三十秒,并且,大多數(shù)對象一般在幾秒內(nèi)就會變?yōu)槔?/p>
如果對象這么長時間都沒被回收,比如2分鐘沒有回收,可以認(rèn)為這些對象是會存活的比較長的對象,從而移動到老年代,而不是繼續(xù)一直占用survivor區(qū)空間。
所以,可以將默認(rèn)的15歲改小一點,比如改為5,
那么意味著對象要經(jīng)過5次minor gc才會進入老年代,整個時間也有一兩分鐘了(5*30s= 150s),和幾秒的時間相比,對象已經(jīng)存活了足夠長時間了。
所以:可以適當(dāng)調(diào)整JVM參數(shù)如下:
step6:多大的對象,可以直接到老年代比較合適?
對于多大的對象直接進入老年代(參數(shù)-XX:PretenureSizeThreshold),一般可以結(jié)合自己系統(tǒng)看下有沒有什么大對象 生成,預(yù)估下大對象的大小,一般來說設(shè)置為1M就差不多了,很少有超過1M的大對象,
所以:可以適當(dāng)調(diào)整JVM參數(shù)如下:
step7:垃圾回收器CMS老年代的參數(shù)優(yōu)化
JDK8默認(rèn)的垃圾回收器是-XX:+UseParallelGC(年輕代)和-XX:+UseParallelOldGC(老年代),
如果內(nèi)存較大(超過4個G,只是經(jīng)驗 值),還是建議使用G1.
這里是4G以內(nèi),又是主打“低延時” 的業(yè)務(wù)系統(tǒng),可以使用下面的組合:
新生代的采用ParNew回收器,工作流程就是經(jīng)典復(fù)制算法,在三塊區(qū)中進行流轉(zhuǎn)回收,只不過采用多線程并行的方式加快了MinorGC速度。
老生代的采用CMS。再去優(yōu)化老年代參數(shù):比如老年代默認(rèn)在標(biāo)記清除以后會做整理,還可以在CMS的增加GC頻次還是增加GC時長上做些取舍,
如下是響應(yīng)優(yōu)先的參數(shù)調(diào)優(yōu):
設(shè)定CMS在對內(nèi)存占用率達(dá)到70%的時候開始GC(因為CMS會有浮動垃圾,所以一般都較早啟動GC)
和上面搭配使用,否則只生效一次。
強制操作系統(tǒng)把內(nèi)存真正分配給IVM,而不是用時才分配。
綜上,只要年輕代參數(shù)設(shè)置合理,老年代CMS的參數(shù)設(shè)置基本都可以用默認(rèn)值,如下所示:
參數(shù)解釋
1.‐Xms3072M ‐Xmx3072M 最小最大堆設(shè)置為3g,最大最小設(shè)置為一致防止內(nèi)存抖動
2.‐Xss1M 線程棧1m
3.‐Xmn2048M ‐XX:SurvivorRatio=8 年輕代大小2g,eden與survivor的比例為8:1:1,也就是1.6g:0.2g:0.2g
4.-XX:MaxTenuringThreshold=5 年齡為5進入老年代 5.‐XX:PretenureSizeThreshold=1M 大于1m的大對象直接在老年代生成
6.‐XX:+UseParNewGC ‐XX:+UseConcMarkSweepGC 使用ParNew+cms垃圾回收器組合
7.‐XX:CMSInitiatingOccupancyFractinotallow=70 老年代中對象達(dá)到這個比例后觸發(fā)fullgc
8.‐XX:+UseCMSInitiatinpOccupancyOnly
9.‐XX:+AlwaysPreTouch 強制操作系統(tǒng)把內(nèi)存真正分配給IVM,而不是用時才分配。
step8:配置OOM時候的內(nèi)存dump文件和GC日志
額外增加了GC日志打印、OOM自動dump等配置內(nèi)容,幫助進行問題排查
在Out Of Memory,JVM快死掉的時候,輸出Heap Dump到指定文件。
不然開發(fā)很多時候還真不知道怎么重現(xiàn)錯誤。
路徑只指向目錄,JVM會保持文件名的唯一性,叫java_pid${pid}.hprof。
因為如果指向特定的文件,而文件已存在,反而不能寫入。
輸出4G的HeapDump,會導(dǎo)致IO性能問題,在普通硬盤上,會造成20秒以上的硬盤IO跑滿,
需要注意一下,但在容器環(huán)境下,這個也會影響同一宿主機上的其他容器。
GC的日志的輸出也很重要:
GC的日志實際上對系統(tǒng)性能影響不大,打日志對排查GC問題很重要。
一份通用的JVM參數(shù)模板
一般來說,大企業(yè)或者架構(gòu)師團隊,都會為項目的業(yè)務(wù)系統(tǒng)定制一份較為通用的JVM參數(shù)模板,但是許多小企業(yè)和團隊可能就疏于這一塊的設(shè)計,如果老板某一天突然讓你負(fù)責(zé)定制一個新系統(tǒng)的JVM參數(shù),你上網(wǎng)去搜大量的JVM調(diào)優(yōu)文章或博客,結(jié)果發(fā)現(xiàn)都是零零散散的、不成體系的JVM參數(shù)講解,根本下不了手,這個時候你就需要一份較為通用的JVM參數(shù)模板了,不能保證性能最佳,但是至少能讓JVM這一層是穩(wěn)定可控的,
在這里給大家總結(jié)了一份模板:
基于4C8G系統(tǒng)的ParNew+CMS回收器模板(響應(yīng)優(yōu)先),新生代大小根據(jù)業(yè)務(wù)靈活調(diào)整!
如果是GC的吞吐優(yōu)先,推薦使用G1,基于8C16G系統(tǒng)的G1回收器模板:
G1收集器自身已經(jīng)有一套預(yù)測和調(diào)整機制了,因此我們首先的選擇是相信它,
即調(diào)整-XX:MaxGCPauseMillis=N參數(shù),這也符合G1的目的——讓GC調(diào)優(yōu)盡量簡單!
同時也不要自己顯式設(shè)置新生代的大?。ㄓ?Xmn或-XX:NewRatio參數(shù)),
如果人為干預(yù)新生代的大小,會導(dǎo)致目標(biāo)時間這個參數(shù)失效。
G1參數(shù) | 描述 | 默認(rèn)值 |
XX:MaxGCPauseMillis=N | 最大GC停頓時間。柔性目標(biāo),JVM滿足90%,不保證100%。 | 200 |
-XX:nitiatingHeapOccupancyPercent=n | 當(dāng)整個堆的空間使用百分比超過這個值時,就會融發(fā)MixGC | 45 |
針對-XX:MaxGCPauseMillis來說,參數(shù)的設(shè)置帶有明顯的傾向性:調(diào)低↓:延遲更低,但MinorGC頻繁,MixGC回收老年代區(qū)減少,增大Full GC的風(fēng)險。調(diào)高↑:單次回收更多的對象,但系統(tǒng)整體響應(yīng)時間也會被拉長。
針對InitiatingHeapOccupancyPercent來說,調(diào)參大小的效果也不一樣:調(diào)低↓:更早觸發(fā)MixGC,浪費cpu。調(diào)高↑:堆積過多代回收region,增大FullGC的風(fēng)險。
調(diào)優(yōu)總結(jié)
系統(tǒng)在上線前的綜合調(diào)優(yōu)思路:
1、業(yè)務(wù)預(yù)估:根據(jù)預(yù)期的并發(fā)量、平均每個任務(wù)的內(nèi)存需求大小,然后評估需要幾臺機器來承載,每臺機器需要什么樣的配置。
2、容量預(yù)估:根據(jù)系統(tǒng)的任務(wù)處理速度,然后合理分配Eden、Surivior區(qū)大小,老年代的內(nèi)存大小。
3、回收器選型:響應(yīng)優(yōu)先的系統(tǒng),建議采用ParNew+CMS回收器;吞吐優(yōu)先、多核大內(nèi)存(heap size≥8G)服務(wù),建議采用G1回收器。
4、優(yōu)化思路:讓短命對象在MinorGC階段就被回收(同時回收后的存活對象<Survivor區(qū)域50%,可控制保留在新生代),長命對象盡早進入老年代,不要在新生代來回復(fù)制;盡量減少Full GC的頻率,避免FGC系統(tǒng)的影響。
5、到目前為止,總結(jié)到的調(diào)優(yōu)的過程主要基于上線前的測試驗證階段,所以我們盡量在上線之前,就將機器的JVM參數(shù)設(shè)置到最優(yōu)!
JVM調(diào)優(yōu)只是一個手段,但并不一定所有問題都可以通過JVM進行調(diào)優(yōu)解決,大多數(shù)的Java應(yīng)用不需要進行JVM優(yōu)化,我們可以遵循以下的一些原則:
- 上線之前,應(yīng)先考慮將機器的JVM參數(shù)設(shè)置到最優(yōu);
- 減少創(chuàng)建對象的數(shù)量(代碼層面);
- 減少使用全局變量和大對象(代碼層面);
- 優(yōu)先架構(gòu)調(diào)優(yōu)和代碼調(diào)優(yōu),JVM優(yōu)化是不得已的手段(代碼、架構(gòu)層面);
- 分析GC情況優(yōu)化代碼比優(yōu)化JVM參數(shù)更好(代碼層面);
通過以上原則,我們發(fā)現(xiàn),其實最有效的優(yōu)化手段是架構(gòu)和代碼層面的優(yōu)化,而JVM優(yōu)化則是最后不得已的手段,也可以說是對服務(wù)器配置的最后一次“壓榨”。
什么是ZGC?
ZGC (Z Garbage Collector)是一款由Oracle公司研發(fā)的,以低延遲為首要目標(biāo)的一款垃圾收集器。
它是基于動態(tài)Region內(nèi)存布局,(暫時)不設(shè)年齡分代,使用了讀屏障、染色指針和內(nèi)存多重映射等技術(shù)來實現(xiàn)可并發(fā)的標(biāo)記-整理算法的收集器。
在 JDK 11 新加入,還在實驗階段,
主要特點是:回收TB級內(nèi)存(最大4T),停頓時間不超過10ms。
優(yōu)點:低停頓,高吞吐量, ZGC 收集過程中額外耗費的內(nèi)存小
缺點:浮動垃圾
目前使用的非常少,真正普及還是需要寫時間的。
如何選擇垃圾收集器?
在真實場景中應(yīng)該如何去選擇呢,下面給出幾種建議,希望對你有幫助:
1、如果你的堆大小不是很大(比如 100MB ),選擇串行收集器一般是效率最高的。參數(shù):-XX:+UseSerialGC 。
2、如果你的應(yīng)用運行在單核的機器上,或者你的虛擬機核數(shù)只有 單核,選擇串行收集器依然是合適的,這時候啟用一些并行收集器沒有任何收益。參數(shù):-XX:+UseSerialGC 。
3、如果你的應(yīng)用是“吞吐量”優(yōu)先的,并且對較長時間的停頓沒有什么特別的要求。選擇并行收集器是比較好的。參數(shù):-XX:+UseParallelGC 。
4、如果你的應(yīng)用對響應(yīng)時間要求較高,想要較少的停頓。甚至 1 秒的停頓都會引起大量的請求失敗,那么選擇 G1 、 ZGC 、 CMS 都是合理的。雖然這些收集器的 GC 停頓通常都比較短,但它需要一些額外的資源去處理這些工作,通常吞吐量會低一些。參數(shù):-XX:+UseConcMarkSweepGC 、 -XX:+UseG1GC 、 -XX:+UseZGC 等。從上面這些出發(fā)點來看,我們平常的 Web 服務(wù)器,都是對響應(yīng)性要求非常高的。
選擇性其實就集中在 CMS、G1、ZGC 上。而對于某些定時任務(wù),使用并行收集器,是一個比較好的選擇。
Hotspot為什么使用元空間替換了永久代?
什么是元空間?什么是永久代?為什么用元空間代替永久代?
我們先回顧一下方法區(qū)吧,看看虛擬機運行時數(shù)據(jù)內(nèi)存圖,如下:
方法區(qū)和堆一樣,是各個線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯后的代碼等數(shù)據(jù)。
什么是永久代?它和方法區(qū)有什么關(guān)系呢?
如果在HotSpot虛擬機上開發(fā)、部署,很多程序員都把方法區(qū)稱作永久代。
可以說方法區(qū)是規(guī)范,永久代是Hotspot針對該規(guī)范進行的實現(xiàn)。
在Java7及以前的版本,方法區(qū)都是永久代實現(xiàn)的。
什么是元空間?它和方法區(qū)有什么關(guān)系呢?
對于Java8,HotSpots取消了永久代,取而代之的是元空間(Metaspace)。
換句話說,就是方法區(qū)還是在的,只是實現(xiàn)變了,從永久代變?yōu)樵臻g了。
為什么使用元空間替換了永久代?
永久代的方法區(qū),和堆使用的物理內(nèi)存是連續(xù)的。
永久代是通過以下這兩個參數(shù)配置大小的~
- -XX:PremSize:設(shè)置永久代的初始大小
- -XX:MaxPermSize: 設(shè)置永久代的最大值,默認(rèn)是64M
對于永久代,如果動態(tài)生成很多class的話,就很可能出現(xiàn)java.lang.OutOfMemoryError:PermGen space錯誤,因為永久代空間配置有限嘛。最典型的場景是,在web開發(fā)比較多jsp頁面的時候。
JDK8之后,方法區(qū)存在于元空間(Metaspace)。
物理內(nèi)存不再與堆連續(xù),而是直接存在于本地內(nèi)存中,理論上機器內(nèi)存有多大,元空間就有多大。
可以通過以下的參數(shù)來設(shè)置元空間的大?。?/p>
- -XX:MetaspaceSize,初始空間大小,達(dá)到該值就會觸發(fā)垃圾收集進行類型卸載,同時GC會對該值進行調(diào)整:如果釋放了大量的空間,就適當(dāng)降低該值;如果釋放了很少的空間,那么在不超過MaxMetaspaceSize時,適當(dāng)提高該值。
- -XX:MaxMetaspaceSize,最大空間,默認(rèn)是沒有限制的。
- -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空間容量的百分比,減少為分配空間所導(dǎo)致的垃圾收集
- -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空間容量的百分比,減少為釋放空間所導(dǎo)致的垃圾收集
所以,為什么使用元空間替換永久代?
表面上看是為了避免OOM異常。
因為通常使用PermSize和MaxPermSize設(shè)置永久代的大小就決定了永久代的上限,但是不是總能知道應(yīng)該設(shè)置為多大合適, 如果使用默認(rèn)值很容易遇到OOM錯誤。
當(dāng)使用元空間時,可以加載多少類的元數(shù)據(jù)就不再由MaxPermSize控制, 而由系統(tǒng)的實際可用空間來控制啦。
什么是Stop The World ? 什么是OopMap?什么是安全點?
進行垃圾回收的過程中,會涉及對象的移動。
為了保證對象引用更新的正確性,必須暫停所有的用戶線程,像這樣的停頓,虛擬機設(shè)計者形象描述為Stop The World。也簡稱為STW。
在HotSpot中,有個數(shù)據(jù)結(jié)構(gòu)(映射表)稱為OopMap。
一旦類加載動作完成的時候,HotSpot就會把對象內(nèi)什么偏移量上是什么類型的數(shù)據(jù)計算出來,記錄到OopMap。
在即時編譯過程中,也會在特定的位置生成 OopMap,記錄下棧上和寄存器里哪些位置是引用。
這些特定的位置主要在:1.循環(huán)的末尾(非 counted 循環(huán))
2.方法臨返回前 / 調(diào)用方法的call指令后
3.可能拋異常的位置
這些位置就叫作安全點(safepoint)。
用戶程序執(zhí)行時并非在代碼指令流的任意位置都能夠在停頓下來開始垃圾收集,而是必須是執(zhí)行到安全點才能夠暫停。