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