(十一)JVM成神路之性能調(diào)優(yōu)篇:GC調(diào)優(yōu)刻肄、Arthas工具詳解及各場景下線上最佳配置推薦

引言

? ?“在當(dāng)前的互聯(lián)網(wǎng)開發(fā)模式下瓤球,系統(tǒng)訪問量日漲、并發(fā)暴增敏弃、線上瓶頸等各種性能問題紛涌而至卦羡,性能優(yōu)化成為了現(xiàn)時(shí)代開發(fā)過程中炙手可熱的名詞,無論是在開發(fā)麦到、面試過程中绿饵,性能優(yōu)化都是一個(gè)常談常新的話題”。Java語言作為企業(yè)應(yīng)用中的“抗鼎者”瓶颠,Java生態(tài)中也積攢了大量寶貴的性能優(yōu)化經(jīng)驗(yàn)拟赊。
? ?在應(yīng)用系統(tǒng)中,性能優(yōu)化其實(shí)可以從各個(gè)角度出發(fā)考慮粹淋,如架構(gòu)優(yōu)化吸祟、前端調(diào)優(yōu)、中間件調(diào)優(yōu)桃移、網(wǎng)關(guān)調(diào)優(yōu)欢搜、容器調(diào)優(yōu)、JVM調(diào)優(yōu)谴轮、接口調(diào)優(yōu)炒瘟、服務(wù)器調(diào)優(yōu)、數(shù)據(jù)庫調(diào)優(yōu)等第步,從優(yōu)化類型上而言疮装,主體可以分為三類:

  • 結(jié)構(gòu)/架構(gòu)優(yōu)化:優(yōu)化應(yīng)用系統(tǒng)整體架構(gòu)做到性能提升的目的缘琅。如:讀寫分離、集群熱備廓推、分布式架構(gòu)刷袍、引入緩存/消息/搜索中間件、分庫分表樊展、中臺架構(gòu)(大數(shù)據(jù)中臺呻纹、基礎(chǔ)設(shè)施中臺)等。
  • 配置/參數(shù)優(yōu)化:調(diào)整應(yīng)用系統(tǒng)中各層面的配置文件专缠、啟動(dòng)參數(shù)達(dá)到優(yōu)化性能的目標(biāo)雷酪。如:JVM、服務(wù)器涝婉、數(shù)據(jù)庫哥力、、操作系統(tǒng)墩弯、中間件吩跋、容器、網(wǎng)關(guān)參數(shù)調(diào)整等渔工。
  • 代碼/操作優(yōu)化:開發(fā)者編寫程序時(shí)锌钮,從代碼、操作方面進(jìn)行調(diào)節(jié)引矩,達(dá)到效率更高的初衷轧粟。如:代碼中使用更優(yōu)秀的算法思想/設(shè)計(jì)模式、SQL優(yōu)化脓魏、對中間件的操作優(yōu)化等。

本章則重點(diǎn)闡述Java中通惫,JVM虛擬機(jī)相關(guān)的全面優(yōu)化茂翔,如:內(nèi)存、GC履腋、即時(shí)編譯珊燎、JVM參數(shù)配置等。

一遵湖、系統(tǒng)中性能優(yōu)化的核心思維

? ?性能調(diào)優(yōu)與上章:《線上排查問題》一樣悔政,是建立在經(jīng)驗(yàn)的基礎(chǔ)之上才能做好的,對于調(diào)優(yōu)要實(shí)事求是延旧,任何的調(diào)優(yōu)手段或技巧不要紙上談兵谋国,只有經(jīng)過實(shí)踐的才能用于生產(chǎn)環(huán)境,千萬不要將一些沒有實(shí)際依據(jù)的調(diào)優(yōu)策略用于線上環(huán)境迁沫,否則可能會(huì)導(dǎo)致原本好好的程序反而調(diào)優(yōu)調(diào)崩潰芦瘾。

1.1捌蚊、單個(gè)節(jié)點(diǎn)層面調(diào)優(yōu)的核心思想

? ?在一個(gè)程序中,所有的業(yè)務(wù)執(zhí)行實(shí)體都為線程近弟,應(yīng)用程序的性能跟線程是直接掛鉤的缅糟。而程序中的一條線程必須要經(jīng)過CPU的調(diào)度才可執(zhí)行,線程執(zhí)行時(shí)必然也會(huì)需要數(shù)據(jù)祷愉、產(chǎn)生數(shù)據(jù)窗宦,最終也會(huì)和內(nèi)存、磁盤打交道二鳄。因而單個(gè)節(jié)點(diǎn)的性能表現(xiàn)赴涵,不可避免的會(huì)跟CPU、內(nèi)存泥从、磁盤沾上關(guān)系句占。
? ?線程越多,需要的CPU調(diào)度能力也就越強(qiáng)躯嫉,需要的內(nèi)存也越大纱烘,磁盤IO速率也會(huì)要求越快。因此CPU祈餐、內(nèi)存擂啥、磁盤,這三者之間的任意之一達(dá)到了瓶頸帆阳,程序中的線程數(shù)量也會(huì)達(dá)到極限哺壶。達(dá)到極限后,系統(tǒng)的性能會(huì)成拋物線式下滑蜒谤,從而可能導(dǎo)致系統(tǒng)整體性能下降乃至癱瘓山宾。

由于如上原因,在考慮性能優(yōu)化時(shí)鳍徽,必然不能讓CPU资锰、內(nèi)存、磁盤等資源的使用率達(dá)到95%+阶祭,一般而言绷杜,最大利用率控制在80-85%左右的最佳狀態(tài)。

? ?同時(shí)濒募,前面也分析過鞭盟,因?yàn)槌绦虻男阅芨€程掛鉤,所以線程的模型也是影響性能的重要因素瑰剃。目前程序設(shè)計(jì)中主要存在三種線程處理模型:BIO齿诉、NIO、AIO(NIO2)BIO是Java中傳統(tǒng)的線程一對一處理模型鹃两,NIO的最佳實(shí)踐為reactor模型遗座,而proactor模型又作為了NIO2/AIO的落地者。絕大部分情況下俊扳,AIO的性能優(yōu)于NIO途蒋,而NIO的性能又遠(yuǎn)超于BIO

所以在做性能優(yōu)化時(shí)馋记,你應(yīng)該要清楚系統(tǒng)的性能瓶頸在哪兒号坡,到底是要調(diào)哪個(gè)位置?是線程模型梯醒?或是CPU調(diào)度宽堆?還是內(nèi)存回收?亦是磁盤IO速率茸习?針對不同層面有不同的優(yōu)化方案畜隶,并非為了追求“熱詞/潮流”而盲目的調(diào)優(yōu)。

1.2号胚、優(yōu)秀且適用的系統(tǒng)架構(gòu)勝過千萬次調(diào)優(yōu)

? ?一個(gè)單體架構(gòu)(Tomcat+MySQL)部署的系統(tǒng)遇到性能問題時(shí)籽慢,能力再強(qiáng),本事再大猫胁,任憑使出渾身解數(shù)也無法將其調(diào)到處理萬級并發(fā)的程序箱亿,正常服務(wù)器部署的一臺MySQL服務(wù)做到極致調(diào)優(yōu)也難以在一秒內(nèi)承載5000+QPS。一味的追求極致的優(yōu)化弃秆,其實(shí)也難以解決真正大流量下的并發(fā)沖擊届惋,因此一套優(yōu)秀的系統(tǒng)架構(gòu)勝過自己千萬次的調(diào)優(yōu)痕届。
? ?當(dāng)然隆豹,也并非說項(xiàng)目實(shí)現(xiàn)時(shí)财饥,越多的技術(shù)加進(jìn)來越好久锥,一套完善的分布式架構(gòu)就必然比單體架構(gòu)要好嗎?其實(shí)也不見得个盆,因?yàn)楫?dāng)引入的技術(shù)越多讨越,所需要考慮的問題也會(huì)更多远搪,耗費(fèi)的成本也會(huì)越高峡捡,一個(gè)項(xiàng)目收益60W,結(jié)果用上最好的配置(高端的開發(fā)者+頂級的服務(wù)器+完善的分布式架構(gòu))成本耗費(fèi)200W筑悴,這值得嗎们拙?答案顯而易見。因此阁吝,并沒有最好的技術(shù)架構(gòu)砚婆,只有最適用的架構(gòu),能從現(xiàn)有環(huán)境及實(shí)際業(yè)務(wù)出發(fā),選用最為合適的技術(shù)體系装盯,這才是我們應(yīng)該做的事情坷虑。如:

  • 項(xiàng)目業(yè)務(wù)中讀寫參半,單節(jié)點(diǎn)難以承載壓力埂奈,項(xiàng)目集群迄损、雙主熱備值得參考。
  • 項(xiàng)目業(yè)務(wù)中寫大于讀账磺,引入消息中間件芹敌、DB分庫、項(xiàng)目集群也可以考慮垮抗。
  • 項(xiàng)目業(yè)務(wù)中讀大于寫氏捞,引入緩存/搜索中間件、動(dòng)靜分離冒版、讀寫分離是些不錯(cuò)的選擇液茎。
  • .......

當(dāng)你的系統(tǒng)原有架構(gòu)遇到性能瓶頸時(shí),你甚至可以考慮進(jìn)一步做架構(gòu)優(yōu)化辞嗡,如:設(shè)計(jì)多級分布式緩存捆等、緩存中間件做集群、消息中間件做集群欲间、Java程序做集群楚里、數(shù)據(jù)庫做分庫分表、搜索中間件做集群.....猎贴,慢慢的班缎,你的系統(tǒng)會(huì)越來越龐大復(fù)雜,需要處理的問題也更為棘手她渴,但帶來的效果也顯而易見达址,隨著系統(tǒng)的結(jié)構(gòu)不斷變化,承載百萬級趁耗、千萬級沉唠、億級、乃至更大級別的流量也并非難事苛败。

? ?但只有當(dāng)你的業(yè)務(wù)流量/訪問壓力在選用其他架構(gòu)無法承載時(shí)满葛,你才應(yīng)該考慮更為龐大的架構(gòu)。當(dāng)然罢屈,如果項(xiàng)目在起步初期就有預(yù)估會(huì)承載巨大的流量壓力嘀韧,那么提前考慮也很在理,采用分布式/微服務(wù)架構(gòu)也并非失策缠捌,因?yàn)閷Ρ绕渌軜?gòu)體系而言锄贷,微服務(wù)架構(gòu)的拓展性更為靈活。但也需要記住:分布式/微服務(wù)體系是很好谊却,但它不一定適用于你的項(xiàng)目柔昼。

1.3、預(yù)防大于一切炎辨,調(diào)優(yōu)并非“臨時(shí)抱佛腳”

? ?當(dāng)問題出現(xiàn)時(shí)再想辦法解決捕透,這種策略永遠(yuǎn)都屬于下下策,防范于未然才是最佳方案蹦魔,提前防范問題出現(xiàn)主要可分為兩個(gè)階段:

  • ①項(xiàng)目初期預(yù)測未來的流量壓力激率,提前根據(jù)業(yè)務(wù)設(shè)計(jì)出合適的架構(gòu),確保上線后可以承載業(yè)務(wù)的正常增長勿决。
  • ②項(xiàng)目上線后乒躺,配備完善的監(jiān)控系統(tǒng),在性能瓶頸來臨前設(shè)好警報(bào)線低缩,確保能夠在真正的性能瓶頸到來之前解決問題嘉冒。

對于項(xiàng)目初期的架構(gòu)思考,值得牢記的一點(diǎn)是:不要“卡點(diǎn)”設(shè)計(jì)咆繁,也不能過度設(shè)計(jì)造成性能過剩讳推,舉例:

項(xiàng)目上線后的正常情況下,流量大概在“一木桶”左右玩般,結(jié)果你設(shè)計(jì)時(shí)直接整出個(gè)“池塘”級別的結(jié)構(gòu)出來了银觅,這顯然是不合理的,畢竟架構(gòu)體系越龐大坏为,項(xiàng)目的成本也自然就越高究驴。
當(dāng)然,也不能說正常情況下壓力在“一木桶”左右匀伏,就只設(shè)計(jì)出一套僅能夠承載“一木桶”流量的結(jié)構(gòu)洒忧,這種“卡點(diǎn)”設(shè)計(jì)的策略也是不可取的,因?yàn)槟阈枰m當(dāng)考慮業(yè)務(wù)增長帶來的風(fēng)險(xiǎn)够颠,如果“卡點(diǎn)”設(shè)計(jì)熙侍,那么很容易讓項(xiàng)目上線后,短期內(nèi)就遭遇性能瓶頸履磨。
因此蛉抓,如果項(xiàng)目正常的訪問壓力大概在“桶”級別,那將結(jié)構(gòu)設(shè)計(jì)到“缸”級別是合理的剃诅,這樣即不必?fù)?dān)心過度設(shè)計(jì)帶來的性能過剩巷送,導(dǎo)致成本增高;也無需考慮卡點(diǎn)設(shè)計(jì)造成的:項(xiàng)目短期遭遇性能瓶頸综苔。
但設(shè)計(jì)時(shí)的這個(gè)度惩系,必須由你自己根據(jù)項(xiàng)目的業(yè)務(wù)場景和環(huán)境去思量,不存在前篇一律的方法可教如筛。

有人曾說過:“如果你可以根據(jù)業(yè)務(wù)情景設(shè)計(jì)出一套能確保業(yè)務(wù)增長堡牡,且在線上能穩(wěn)定運(yùn)行三年時(shí)間以上的結(jié)構(gòu),那你就是位業(yè)內(nèi)的頂尖架構(gòu)”杨刨,但老話說的好:“計(jì)劃永遠(yuǎn)趕不上變化”晤柄,就算思考到業(yè)務(wù)的每個(gè)細(xì)節(jié),也不可能設(shè)計(jì)出一套一勞永逸的結(jié)構(gòu)出現(xiàn)妖胀,我們永遠(yuǎn)無法判斷意外和明天哪個(gè)先來芥颈。因而,項(xiàng)目上線后赚抡,配備完善的監(jiān)控警報(bào)系統(tǒng)也是必不可少的爬坑。不過值得注意的是:

監(jiān)控系統(tǒng)的作用并不是用來提醒你項(xiàng)目“嗝屁”了的,而是用來提醒你:線上部署的應(yīng)用系統(tǒng)可能會(huì)“嗝屁”或快“嗝屁”了涂臣,畢竟當(dāng)項(xiàng)目災(zāi)難已經(jīng)發(fā)生時(shí)再給警報(bào)盾计,那到時(shí)候的情況就是:“亡羊補(bǔ)牢,為時(shí)已晚”赁遗。
通常情況下署辉,在監(jiān)控系統(tǒng)上面設(shè)置的性能閾值都會(huì)比最大極限值要低5~15%,如:最大極限值是85%岩四,那設(shè)置告警值一般是75%左右就會(huì)告警哭尝,不會(huì)真達(dá)到85%才告警,只有這樣做才能留有足夠的時(shí)間讓運(yùn)維和開發(fā)人員介入排查剖煌。當(dāng)系統(tǒng)發(fā)出可能“嗝屁”的警告時(shí)材鹦,開發(fā)和運(yùn)維人員就應(yīng)當(dāng)立即排查相關(guān)的故障隱患,然后再通過不斷的修改和優(yōu)化末捣,提前將可能會(huì)出現(xiàn)的性能瓶頸解決侠姑,這才是性能調(diào)優(yōu)的正確方案。
因此箩做,最終結(jié)論為:絕不能等到系統(tǒng)奔潰才去優(yōu)化莽红,預(yù)防勝于一切

1.4邦邦、無需追求完美安吁,理性權(quán)衡利弊

? ?“追求極致,做到完美”這點(diǎn)是大部分開發(fā)者的通病燃辖,很多人會(huì)因?yàn)檫@個(gè)思想導(dǎo)致自己在面臨一些問題時(shí)束手無策鬼店,比如舉個(gè)例子:

業(yè)務(wù):MacBookPro一元購活動(dòng),預(yù)計(jì)訪問壓力:10000QPS黔龟。
環(huán)境:單臺機(jī)器只能承載2000QPS妇智,目前機(jī)房中還剩余兩臺空閑服務(wù)器滥玷。
狀況:此時(shí)就算將空閑的兩臺機(jī)器加上去,也無法頂住目前的訪問壓力巍棱。
此時(shí)你會(huì)怎么做惑畴?很多人都會(huì)茫然,這看起來好像是沒辦法的事情呀航徙,似乎只能等死了.....

但事實(shí)真的如此嗎如贷?并非如此,其實(shí)這種情況也有多種解決方案到踏,如:

  • ①停掉系統(tǒng)中部分非核心的業(yè)務(wù)杠袱,將服務(wù)器資源暫時(shí)讓給該業(yè)務(wù)。
  • ②拋棄掉部分用戶的請求窝稿,只接受處理部分用戶的請求楣富。
  • ③........

這些方案是不是可以解決上面的哪個(gè)問題呢?答案是肯定的伴榔。但完美主義者會(huì)認(rèn)為:
? ?系統(tǒng)中的服務(wù)不能停啊菩彬,得保持正常服務(wù)啊。
? ?用戶的請求怎么能拋潮梯,用戶的訪問必須得響應(yīng)啊骗灶。
但事實(shí)告訴你的是:類似于京東、淘寶秉馏、12306等這些國內(nèi)的頂級大廠耙旦,也照樣是這么干的。好比阿里萝究,在雙十一的時(shí)候都會(huì)抽調(diào)很多冷門業(yè)務(wù)的服務(wù)器資源給淘寶使用免都,也包括你在參與這些電商平臺的搶購或秒殺類活動(dòng)時(shí),你是否遇到過如下情況:

  • 服務(wù)器繁忙帆竹,請稍后重試......
  • 服務(wù)器已滿绕娘,排隊(duì)中.....
  • 前方擁堵,排隊(duì)中栽连,當(dāng)前第x位.....

如果當(dāng)你遇到了這些情況险领,答案顯而易見,你的請求壓根就沒有到后端秒紧,在前端就給你pass了绢陌,然后給你返回了一個(gè)字符串,讓你傻傻的等待熔恢。

? ?這個(gè)例子要告訴大家的是:在處理棘手問題或優(yōu)化性能時(shí)脐湾,無需刻意追求完美,理性權(quán)衡利弊后叙淌,適當(dāng)?shù)淖龀鲆恍Q斷秤掌,拋棄掉一部分不重要的愁铺,起碼比整個(gè)系統(tǒng)掛掉要好,何況之后照樣也可以恢復(fù)闻鉴。

1.5帜讲、性能調(diào)優(yōu)的通核心步驟

? ?性能優(yōu)化永遠(yuǎn)是建立在性能瓶頸之上的,如果你的系統(tǒng)沒有出現(xiàn)瓶頸椒拗,那則無需調(diào)優(yōu),調(diào)優(yōu)之前需要牢記的一點(diǎn)是:不要為了調(diào)優(yōu)而調(diào)優(yōu)获黔,而是需要調(diào)優(yōu)時(shí)才調(diào)蚀苛。
? ?而發(fā)現(xiàn)性能瓶頸的方式有兩種,一種是你的應(yīng)用中具備完善的監(jiān)控系統(tǒng)玷氏,能夠提前感知性能瓶頸的出現(xiàn)堵未。另一種則是:應(yīng)用中沒有搭載監(jiān)控系統(tǒng),性能瓶頸已經(jīng)發(fā)生盏触,從而導(dǎo)致應(yīng)用頻繁宕機(jī)渗蟹。大型的系統(tǒng)一般都會(huì)搭載完善的監(jiān)控系統(tǒng),但大多數(shù)中小型項(xiàng)目卻不具備該條件赞辩,因此雌芽,大部分中小型項(xiàng)目發(fā)現(xiàn)性能瓶頸時(shí),大多數(shù)情況下已經(jīng)“嗝屁”了辨嗽。

? ?通常而言世落,性能優(yōu)化的步驟可分為如下幾步:

  • ①發(fā)現(xiàn)性能瓶頸:如有監(jiān)控系統(tǒng),那它會(huì)主動(dòng)發(fā)出警報(bào)糟需;如若沒有屉佳,那出現(xiàn)瓶頸時(shí)應(yīng)用肯定會(huì)出問題,如:無響應(yīng)洲押、響應(yīng)緩慢武花、頻繁宕機(jī)等。
  • ②排查瓶頸原因:排查瓶頸是由于故障問題導(dǎo)致的杈帐,還是真的存在性能瓶頸体箕。
  • ③定位瓶頸位置:往往一個(gè)系統(tǒng)都會(huì)由多個(gè)層面協(xié)同工作,然后對外提供服務(wù)挑童,當(dāng)發(fā)現(xiàn)性能瓶頸時(shí)干旁,應(yīng)當(dāng)確定瓶頸的范圍,如:網(wǎng)絡(luò)帶寬瓶頸炮沐、Java應(yīng)用瓶頸争群、數(shù)據(jù)庫瓶頸等。
  • ④解決性能瓶頸:定位到具體的瓶頸后對癥下藥大年,從結(jié)構(gòu)换薄、配置玉雾、操作等方面出發(fā),著手解決瓶頸問題轻要。

本章則重點(diǎn)是闡述Java虛擬機(jī)-JVM相關(guān)的調(diào)優(yōu)操作复旬,但需要先提前說明的是:

單層面的性能調(diào)優(yōu)其實(shí)只能當(dāng)成錦上添花的作用,但絕對不能成為系統(tǒng)性能高/低冲泥、響應(yīng)快/慢驹碍、吞吐量大/小的決定性要素。應(yīng)用系統(tǒng)的性能本身就還算可以凡恍,那么調(diào)優(yōu)的作用是讓其性能更佳志秃。但如若項(xiàng)目結(jié)構(gòu)本身就存在問題,那么能夠帶來的性能提升也是有限的嚼酝,如果你想讓你的項(xiàng)目快到飛起浮还,那么還需要從多個(gè)層面共同著手才能達(dá)到目的。

二闽巩、JVM垃圾收集相關(guān)調(diào)優(yōu)策略

? ?在JVM垃圾收集相關(guān)的調(diào)優(yōu)實(shí)踐中钧舌,通常都是以最優(yōu)吞吐量和最短停頓時(shí)間來評價(jià)JVM的性能:吞吐量越高代表性能越好、暫停時(shí)間越短也代表越好涎跨。那么如何做到這兩點(diǎn)呢洼冻?核心思想在于:

  • 盡可能讓對象在新生代中分配和回收。
  • 盡量避免過多對象進(jìn)入年老代隅很,縮短年老代GC時(shí)間碘赖。
  • 盡量給JVM分配足夠多的內(nèi)存,減少所有區(qū)域中的GC次數(shù)外构。

? ?歸根結(jié)底普泡,本質(zhì)思想就一點(diǎn):“盡量讓Java中的對象去到它自己該去的位置”,短命的對象就老老實(shí)實(shí)的進(jìn)入新生代區(qū)域审编,大對象和長命的對象則進(jìn)入年老代空間撼班,避免JVM因?yàn)閷ο蟆皝y竄”導(dǎo)致GC頻發(fā)和GC時(shí)間變長,如:

  • 本該在新生代的短命對象由于特殊原因進(jìn)了年老代垒酬,導(dǎo)致年老代GC次數(shù)變多/時(shí)間變長砰嘁。
  • 本該直接分配在年老代的長命大對象,因?yàn)槟承┰蛉勘环峙湓谛律本浚瑢?dǎo)致新生代可分配空間變少矮湘,引發(fā)分配擔(dān)保機(jī)制,造成大量未達(dá)到標(biāo)準(zhǔn)的新生代對象提前進(jìn)入年老代口糕。

因此缅阳,GC調(diào)優(yōu)的目的就相當(dāng)于給JVM做“保養(yǎng)”,讓其每個(gè)區(qū)域按照設(shè)計(jì)的初衷正常工作景描。

? ?通常情況下十办,當(dāng)JVM存在性能問題時(shí)秀撇,都會(huì)牽扯到兩個(gè)概念,分配速率(Allocation Rate)和提升速率(Promotion Rate)向族,這也是分析性能問題時(shí)常用的兩個(gè)指標(biāo)呵燕,其中分配速率影響新生代的垃圾回收,提升速率影響年老代的垃圾回收件相。

2.1再扭、新生代-分配速率(Allocation Rate)

? ?分配速率代表固定時(shí)間內(nèi)分配的內(nèi)存量,通常情況下以MB/S為單位夜矗,分配速率高泛范,其實(shí)并不是什么好事,對于這點(diǎn)我們稍后再做闡述侯养。先來具體如何計(jì)算分配的速率。

2.1.1澄干、分配速率如何計(jì)算逛揩?

一般而言可以通過GC日志計(jì)算出來,比如:

0.751: [GC (Allocation Failure) [PSYoungGen: 30705K->5115K(38400K)]
    30705K->12385K(125952K), 0.0187498 secs]
    [Times: user=0.00 sys=0.00, real=0.02 secs] 
1.514: [GC (Allocation Failure) [PSYoungGen: 38395K->5120K(71680K)]
    45665K->35687K(159232K), 0.0570688 secs] 
    [Times: user=0.09 sys=0.00, real=0.06 secs] 
3.018: [GC (Allocation Failure) [PSYoungGen: 70326K->5104K(71680K)]
    108940K->105240K(172032K), 0.0866792 secs] 
    [Times: user=0.30 sys=0.02, real=0.09 secs] 

分配速率計(jì)算公式:(本輪GC前使用容量-上輪GC后使用容量)/(本輪GC時(shí)間-上輪GC時(shí)間)

GC輪數(shù) 時(shí)間差值 上輪GC后容量 本輪GC前容量 容量差值 分配速率
第一輪 751ms 0KB 30705KB 30705KB ≈41MB/S
第二輪 763ms 5115KB 38395KB 33280KB ≈44MB/S
第三輪 1504ms 5120KB 70326KB 65206KB ≈43MB/S
每輪均速 NULL NULL NULL NULL ≈43MB/S

通過GC日志中的信息可以初步計(jì)算出麸俘,該Java程序中的對象分配速率大概在43MB/S左右辩稽。

2.1.2、分配速率對JVM的影響

? ?前面曾提及過从媚,分配速率高并不是好事逞泄,為什么這么說呢?因?yàn)镴ava程序的分配速率越高時(shí)拜效,也代表著堆中分配的對象會(huì)越多喷众,對象越多也就會(huì)讓GC的頻率更頻繁。因此紧憾,當(dāng)分配速率越高到千,會(huì)導(dǎo)致JVM的GC開銷越大,分配速率的變化會(huì)增加或降低STW的頻率赴穗,從而影響吞吐量憔四。

但高分配速率的標(biāo)準(zhǔn)是相對而言的,要根據(jù)具體的Eden區(qū)大小來判斷般眉,一個(gè)堆大小為32GB的分配速率是1000MB/S了赵,一個(gè)500MB的堆空間分配速率為100MB/S,前者可被稱為是高分配速率嗎甸赃?并非如此柿汛,因?yàn)榍罢叩亩延?code>32G,1000MB/S的速率也需要一段時(shí)間才能觸發(fā)GC埠对,但后者100MB/S的速率對于500M的堆空間而言苛茂,則可被稱為高速率已烤,因?yàn)閷τ?code>500MB的堆空間而言,會(huì)在極短的時(shí)間內(nèi)觸發(fā)GC妓羊。因此胯究,分配速率高低是要根據(jù)實(shí)際的堆大小來判斷。

2.1.3躁绸、分配速率的四種狀況

  • ①分配速率低裕循,回收速率超于分配速率,GC狀態(tài)無異常净刮,代表系統(tǒng)GC正常剥哑。
  • ②分配速率高,回收速率略低于或遠(yuǎn)低于分配速率淹父,代表程序存在OOM隱患株婴。
  • ③分配速率高,但回收速率勉強(qiáng)可以跟上暑认,代表系統(tǒng)處于“亞健康”狀態(tài)困介。
  • ④分配速率低,GC次數(shù)頻繁蘸际,釋放空間較少座哩,可能存在內(nèi)存泄漏。

? ?其中①為正常狀況粮彤,無需做任何處理根穷,也沒必要去對于這類系統(tǒng)做刻意優(yōu)化,如果你的Java應(yīng)用的JVM處于該狀態(tài)导坟,但程序整體吞吐量依舊上不去屿良,或響應(yīng)速度緩慢,那應(yīng)該從其他層面入手解決惫周。

? ?如果Java應(yīng)用出現(xiàn)第③種情況管引,其實(shí)應(yīng)用本身是沒有任何問題的,這種情況一般是由于分配的堆空間不足闯两,分配速率過快褥伴,導(dǎo)致頻繁觸發(fā)GC回收閾值,因此造成GC負(fù)載過重漾狼,對于這類情況應(yīng)該適當(dāng)調(diào)大堆空間重慢,從而使GC頻繁下降。

? ?②逊躁、④則都是程序中存在隱患會(huì)出現(xiàn)的狀況似踱,通常情況下都是由于程序中存在不規(guī)范的代碼導(dǎo)致的。狀況②是因?yàn)榇a在堆中生成了大量對象,造成分配速率很高核芽,回收速度無法跟上分配速度囚戚,從而導(dǎo)致應(yīng)用有可能內(nèi)存溢出。
? ?狀況④則是明顯的內(nèi)存泄露問題轧简,因?yàn)镚C開銷較大驰坊,但實(shí)際回收后釋放的空間較小,代表內(nèi)存中有大量對象無法回收哮独,這可能是由于內(nèi)存泄漏導(dǎo)致的拳芙。同時(shí),也正因?yàn)镚C次數(shù)比較頻繁皮璧,所以導(dǎo)致應(yīng)用中的用戶線程暫停了工作舟扎,停止了對象分配,因而出現(xiàn)了分配速率低的“假象”悴务。
? ?對于②睹限、④狀況則需要優(yōu)化代碼,前者需要降低分配速率讯檐,后者則需要解決內(nèi)存泄漏羡疗。

2.1.4贡未、新生代空間調(diào)優(yōu)思想

? ?新生代空間的調(diào)優(yōu)核心思想就是需要降低分配速率椅亚,簡單來說就是少創(chuàng)建對象、多分配空間,以減少GC次數(shù)蕉拢,加大系統(tǒng)吞吐量。但需要值得理解的是:為新生代分配更大的堆空間诚亚,反而會(huì)使分配速率提高晕换,但新生代空間大了,觸發(fā)GC的閾值自然會(huì)增加站宗,從而能夠達(dá)到減少GC頻率的目的闸准。

2.2、年老代-提升速率(Promotion Rate)

? ?前面分析的分配速率僅會(huì)對新生代空間造成影響梢灭,而影響年老代空間的則是另外一個(gè)指標(biāo):提升速率夷家,也就是指定時(shí)間內(nèi),新生代升入年老代空間的對象總量敏释,通常單位也為MB/S库快。

在前面談?wù)摲峙渌俾蕰r(shí),可以根據(jù)GC日志計(jì)算新生代的分配占比钥顽,但新生代升入年老代空間的提升速率又該如何計(jì)算呢义屏?因?yàn)?code>MajorGC一般都是伴隨著FullGC一起發(fā)生的,所以無法根據(jù)MajorGC計(jì)算,比較FullGC時(shí)會(huì)回收整堆空間闽铐。

2.2.1蝶怔、提升速率如何計(jì)算?

? ?同樣計(jì)算提升速率時(shí)兄墅,依舊是通過MinorGC日志來計(jì)算:

1.514: [GC (Allocation Failure) [PSYoungGen: 38395K->5120K(71680K)]
    45665K->35687K(159232K), 0.0570688 secs] 
    [Times: user=0.09 sys=0.00, real=0.06 secs] 
3.018: [GC (Allocation Failure) [PSYoungGen: 70326K->5104K(71680K)]
    100894K->105240K(172032K), 0.0866792 secs] 
    [Times: user=0.30 sys=0.02, real=0.09 secs] 

提升速率計(jì)算公式:((新生代回收前使用總量-新生代回收后使用總量)-(整堆回收前使用總量-整堆回收后使用總量))/(本輪GC時(shí)間-上輪GC時(shí)間)

GC輪數(shù) 時(shí)間差值 新生代減少 整堆減少 提升量 提升速率
第一輪 763ms 33275KB 9978KB 23297KB ≈30MB/S
第二輪 1504ms 65222KB 3700KB 61522KB ≈40MB/S
每輪均速 NULL NULL NULL NULL ≈35MB/S

結(jié)果如上表踢星,此刻是通過MinorGC日志來計(jì)算的提升速率,拆解前面的計(jì)算公式可以分析出整體的計(jì)算邏輯:

  • 先通過新生代回收前后的已使用容量大小察迟,計(jì)算出新生代中減少容量斩狱。
  • 再通過整堆回收前后的已使用容量大小,計(jì)算出整個(gè)堆空間的減少容量扎瓶。
  • 再通過新生代減少-整堆減少所踊,這樣可以大致算出新生代中提升到年老代的提升量。
    • 該方式只能計(jì)算出大概的提升量概荷,因?yàn)檎褱p少會(huì)包含年老代秕岛、元空間等區(qū)域回收。
  • 在通過本次GC觸發(fā)時(shí)間-上次GC觸發(fā)時(shí)間误证,得到本輪GC中程序正常執(zhí)行的時(shí)長继薛。
  • 最后通過提示量除執(zhí)行時(shí)長,即可得到JVM的大概提升速率愈捅。

不過在計(jì)算提升速率的時(shí)候遏考,有個(gè)點(diǎn)需要額外注意:Java應(yīng)用啟動(dòng)后的第一條GC日志不能參與計(jì)算,因?yàn)榈谝粭lGC日志是程序啟動(dòng)后蓝谨,初次觸發(fā)GC時(shí)輸出的灌具,此時(shí)堆空間剛從“冷狀態(tài)”啟動(dòng),因此測算出的速率并非程序正常執(zhí)行時(shí)的提升速率譬巫。

2.2.2咖楣、提升速率對JVM的影響

? ?和分配速率相同,提升速率也一樣會(huì)影響GC芦昔,但它影響的是年老代空間诱贿,速率越快也就代表著提升的對象越多,年老代空間被填滿的時(shí)間會(huì)更短咕缎,MajorGC被觸發(fā)的頻率也會(huì)越快珠十。不過通常情況下,年老代的GC一般會(huì)伴隨著FullGC一起發(fā)生凭豪,因此焙蹭,提升速率越高會(huì)最終導(dǎo)致FullGC頻率越快。

2.2.3墅诡、進(jìn)入年老代的三種異常情況

  • ①代碼存在內(nèi)存泄漏

當(dāng)代碼中存在內(nèi)存泄漏時(shí)壳嚎,會(huì)造成堆內(nèi)存被一點(diǎn)點(diǎn)蠶食桐智,最終導(dǎo)致新生代空間沒有空閑內(nèi)存分配新對象,從而觸發(fā)JVM的空間分代擔(dān)保機(jī)制烟馅,開啟對象動(dòng)態(tài)晉升閾值判定说庭,將大量原本未達(dá)晉升標(biāo)準(zhǔn)的對象提前遷入年老代空間,以確保新生代擁有足夠的空閑內(nèi)存維護(hù)Java應(yīng)用的正常執(zhí)行郑趁。
常發(fā)性內(nèi)存泄漏刊驴、偶發(fā)性內(nèi)存泄漏、一次性內(nèi)存泄漏寡润、隱式內(nèi)存泄漏捆憎,不同性質(zhì)的內(nèi)存泄漏造成的提升速率增長也不同,后兩者引發(fā)的速率增長并不大梭纹,但前兩者躲惰,尤其是常發(fā)性內(nèi)存泄漏會(huì)帶來很大的隱患,最終必然會(huì)引發(fā)OOM变抽。

  • ②頻繁的大對象分配

在分代堆中有這么一條法則:“超過指定閾值的大對象會(huì)被直接送往年老代空間”础拨,這條結(jié)論是依據(jù)對象特性而制定的,正常情況下绍载,大對象都不會(huì)是“朝生夕死”的對象诡宗,一般都能夠“活”到成功晉升。因此击儡,為了節(jié)省大對象在兩個(gè)Survivor區(qū)中反復(fù)挪動(dòng)帶來的開銷塔沃,JVM會(huì)將超過閾值標(biāo)準(zhǔn)的大對象直接分配到年老代。
大對象直接進(jìn)入年老代是合理的阳谍,但頻繁的大對象分配是不合理的蛀柴,會(huì)導(dǎo)致年老代被快速填滿,因而頻繁觸發(fā)FullGC边坤。
大對象直接進(jìn)入年老代空間名扛,因此大對象分配是不參與前述的提升速率計(jì)算公式的谅年。

  • ③高并發(fā)/大流量壓力

當(dāng)系統(tǒng)業(yè)務(wù)暴漲時(shí)茧痒,巨大的流量和并發(fā)沖擊會(huì)導(dǎo)致業(yè)務(wù)線程創(chuàng)建更多的新對象,因而會(huì)導(dǎo)致新生代的GC閾值被頻繁觸發(fā)融蹂,加快了新生代整體的晉升速度旺订,從而導(dǎo)致提升速率暴漲。
對于這類正常業(yè)務(wù)增長導(dǎo)致的提升速率變高超燃,這是系統(tǒng)中的常事区拳,這種情況下只需依照具體業(yè)務(wù)流量的增長,合理的調(diào)大堆空間即可意乓。

? ?其實(shí)歸根結(jié)底樱调,上述三點(diǎn)都是在圍繞著“對象被過早提升到年老代”這一核心思想展開。對于年老代而言,新生代空間中的所有對象笆凌,按部就班的活到15歲再晉升是最佳的狀態(tài)圣猎,因?yàn)槟軌蛟谛律具^十多輪GC的對象晉升后,絕大多數(shù)情況下會(huì)再存活很長一段時(shí)間乞而。
? ?但如果是由于上述三種狀況導(dǎo)致對象過早提升到年老代空間送悔,則會(huì)帶來很大的不穩(wěn)定因素,有可能很多提早晉升的對象剛晉升爪模,沒熬過幾輪GC就“死”了欠啤,從而違背了“年老代存放長命對象”的設(shè)計(jì)初衷。同時(shí)屋灌,過早提升還會(huì)造成年老代會(huì)被快速填滿洁段,從而頻繁觸發(fā)FullGC,最終導(dǎo)致Java應(yīng)用暫停時(shí)間過長共郭,影響系統(tǒng)整體的吞吐量眉撵。

2.2.4、年老代空間調(diào)優(yōu)思想

? ?年老代空間調(diào)優(yōu)的核心就一點(diǎn):避免或盡量減少過早提升落塑,為何不是降低提升速率呢纽疟?因?yàn)樵跇I(yè)務(wù)規(guī)模比較大的情況下,提升速率比較高也是合理的憾赁。所以在調(diào)優(yōu)年老代時(shí)污朽,只需要將過早提升的對象依舊控制在新生代即可。

過早提升的表現(xiàn)
  • ①一次FullGC后龙考,年老代的空間占用比極速下降蟆肆。
  • ②短時(shí)間內(nèi)頻繁觸發(fā)FullGC
  • ③提升速率接近分配速率晦款。
  • ④新生代GC發(fā)生后炎功,新生代的空間占用比下降到20%以內(nèi)。
過早提升如何解決缓溅?

? ?處理過早提升時(shí)蛇损,需要根據(jù)具體的情況來決定采取何種措施:

  • ①如果是業(yè)務(wù)或流量壓力變大導(dǎo)致的,那么增大新生代空間即可坛怪。
  • ②如果是代碼中存在問題淤齐,如內(nèi)存泄漏或循環(huán)體中創(chuàng)建對象等,優(yōu)化代碼即可袜匿。
  • ③如果是短命的大對象分配更啄,如大數(shù)組,則可以考慮優(yōu)化數(shù)據(jù)結(jié)構(gòu)居灯,如換成鏈表祭务。

2.3内狗、合理的堆空間該如何分配

? ?Java內(nèi)存各分區(qū)的大小對JVM的性能影響很大,不恰當(dāng)?shù)目臻g大小可能會(huì)埋下很多故障隱患义锥,同時(shí)也會(huì)直接或間接影響JVM的提升速率其屏、分配速率,所以如何將各分區(qū)調(diào)整到合適的大小就成了一個(gè)棘手的問題缨该。大部分不具備線上JVM調(diào)優(yōu)實(shí)操經(jīng)驗(yàn)的開發(fā)者都會(huì)茫然偎行,通常會(huì)認(rèn)為設(shè)定的越大越好,但答案卻并非如此贰拿。
? ?在指定各區(qū)域大小時(shí)蛤袒,可以依據(jù)“活躍數(shù)據(jù)”大小來進(jìn)行設(shè)定,“活躍數(shù)據(jù)”是指應(yīng)用程序穩(wěn)定運(yùn)行后長期存活在堆中的對象膨更,也就是FullGC后年老代中的對象妙真。一般在計(jì)算“活躍數(shù)據(jù)大小”,都會(huì)多次采集程序穩(wěn)定執(zhí)行后的FullGC日志荚守,通過取平均值的方式計(jì)算出堆中長期存活的年老代總量大小珍德。
? ?計(jì)算出“活躍數(shù)據(jù)大小”后,就可以根據(jù)其具體值計(jì)算出其他分區(qū)恰當(dāng)?shù)闹荡Q壤缦拢?/p>

  • ①堆空間:活躍數(shù)據(jù)大小的4~5
  • ②新生代:活躍數(shù)據(jù)大小的1.5~2
  • ③年老代:活躍數(shù)據(jù)大小的2.5~3
  • ④元空間:活躍數(shù)據(jù)大小的1.2~1.8

假設(shè)此時(shí)觀測出的“活躍數(shù)據(jù)大小”為800MB锈候,那堆空間的各區(qū)域的大小:

  • ①堆空間:3200MB
  • ②新生代:1200MB
  • ③年老代:2000MB

當(dāng)然敞贡,這僅作為初始值參考泵琳,具體情況取決于應(yīng)用業(yè)務(wù)的特性和需求。

但需注意的是:實(shí)際過程中誊役,-Xmx获列、-Xms兩個(gè)參數(shù)設(shè)定的值必須一致,這樣做的好處在于可以避免動(dòng)態(tài)伸縮時(shí)帶來的性能損耗與空間震蕩蛔垢,因?yàn)楫?dāng)JVM內(nèi)存不足向OS申請內(nèi)存時(shí)都會(huì)觸發(fā)一次全局GC击孩。

2.4、GC調(diào)優(yōu)實(shí)操思路

? ?前面幾點(diǎn)所提及的都是GC調(diào)優(yōu)的一些方法論以及衡量指標(biāo)鹏漆,但在真正需要處理GC調(diào)優(yōu)時(shí)巩梢,上面幾點(diǎn)只能給你提供輔導(dǎo),并不能建立完善的調(diào)優(yōu)思路甫男,因此且改,接下來再一同論述GC調(diào)優(yōu)的具體實(shí)操思想验烧。

? ?GC調(diào)優(yōu)時(shí)板驳,一般會(huì)根據(jù)Java程序所裝配的垃圾收集器以及具體的GC日志來作為基礎(chǔ)進(jìn)行操作,但不同的垃圾回收器執(zhí)行的GC日志都是不同的碍拆,因此并沒有萬能的調(diào)優(yōu)策略可以滿足所有的性能指標(biāo)若治,GC優(yōu)化要建立在具體的業(yè)務(wù)場景及環(huán)境中慨蓝,才能達(dá)到事半功倍的效果。不過通常GC調(diào)優(yōu)核心步驟如下:

  • ①明確優(yōu)化目標(biāo)
  • ②實(shí)施優(yōu)化操作
  • ③跟蹤優(yōu)化結(jié)果

調(diào)優(yōu)前首先需要確定的就是優(yōu)化目標(biāo)端幼,到底是需要減少GC停頓礼烈,還是增大程序吞吐等,然后再根據(jù)目標(biāo)排除GC日志婆跑,分析后根據(jù)日志中的分配速率此熬、提升速率、GC頻率滑进、GC各階段停頓時(shí)間等指標(biāo)犀忱,實(shí)行具體的優(yōu)化操作。

同時(shí)扶关,也不必奢求一次優(yōu)化到位阴汇,GC調(diào)優(yōu)通常是需要多次進(jìn)行的,一次優(yōu)化往往無法達(dá)到目標(biāo)預(yù)期节槐,需要不斷的根據(jù)優(yōu)化后的GC日志再次制定優(yōu)化策略搀庶,從而最終達(dá)到優(yōu)化目標(biāo)。

但GC調(diào)優(yōu)的根本其實(shí)是在調(diào)“對象”铜异,如果程序本身代碼就存在問題哥倔,好比代碼中存在頻繁創(chuàng)建對象的邏輯,就算你調(diào)出花來也無濟(jì)于事揍庄,必須還得從根源上解決問題未斑,這種情況下應(yīng)當(dāng)采用jmap工具分析堆使用情況,查看對象分布币绩,從而反向定位代碼中的問題并加以解決蜡秽。

2.5、GC優(yōu)化總結(jié)

? ?凡是涉及性能調(diào)優(yōu)的內(nèi)容缆镣,幾乎都必須建立在監(jiān)控系統(tǒng)之上芽突,不一定要全面,但至少能讓調(diào)優(yōu)前有指標(biāo)數(shù)據(jù)可參考董瞻。對于監(jiān)控系統(tǒng)中寞蚌,JVM-GC這塊建議統(tǒng)計(jì)的信息:

  • ①流量方面:流量峰值、流量均值钠糊、用活時(shí)間段等挟秤。
  • ②對象方面:分配速率、每個(gè)請求的分配均值/峰值抄伍、提升速率艘刚、每次提升總量均值等。
  • ③GC方面:MinorGC截珍、FullGC停頓時(shí)長攀甚、GC觸發(fā)間隔箩朴、GC回收總量等。
  • ..........

GC調(diào)優(yōu)時(shí)的收益排序:改善代碼 > 裝配合適的GC回收器 > 重新設(shè)置內(nèi)存比例/大小 > 調(diào)整JVM參數(shù)秋度。

但需重點(diǎn)注意的是:上述的GC調(diào)優(yōu)理論都是基于G1之前的分代垃圾收集器而言的炸庞,G1之后的不分代收集器,如:ZGC荚斯、ShenandoahGC等壓根沒必要刻意優(yōu)化埠居,自身的機(jī)制本就足夠優(yōu)異,而且后續(xù)的不分代收集器對外暴露的可操作參數(shù)也并不多事期。

三拐格、阿里在線排除工具 - Arthas

Arthas工具

? ?Arthas(阿爾薩斯)是阿里開源的一款Java在線診斷工具,官網(wǎng)原話:當(dāng)你遇到以下類似問題而束手無策時(shí)刑赶,Arthas可以幫助你解決:

  • 這個(gè)類從哪個(gè) jar 包加載的捏浊?為什么會(huì)報(bào)各種類相關(guān)的 Exception?
  • 我改的代碼為什么沒有執(zhí)行到撞叨?難道是我沒 commit金踪?分支搞錯(cuò)了?
  • 遇到問題無法在線上 debug牵敷,難道只能通過加日志再重新發(fā)布嗎胡岔?
  • 線上遇到某個(gè)用戶的數(shù)據(jù)處理有問題,但線上同樣無法 debug枷餐,線下無法重現(xiàn)靶瘸!
  • 是否有一個(gè)全局視角來查看系統(tǒng)的運(yùn)行狀況?
  • 有什么辦法可以監(jiān)控到JVM的實(shí)時(shí)運(yùn)行狀態(tài)毛肋?
  • 怎么快速定位應(yīng)用的熱點(diǎn)怨咪,生成火焰圖?
  • 怎樣直接從JVM內(nèi)查找某個(gè)類的實(shí)例润匙?

Arthas支持JDK6+诗眨,支持Linux/Mac/Winodws,采用命令行交互模式孕讳,同時(shí)提供豐富的Tab自動(dòng)補(bǔ)全功能匠楚,進(jìn)一步方便進(jìn)行問題的定位和診斷。

3.1厂财、Arthas快速上手

? ?對于Arthas工具如果不會(huì)使用芋簿,其實(shí)阿里提供的在線的Terminal學(xué)習(xí)方式(傳送門),可以幫助大家快速上手璃饱,下面在本篇中也快速概述一下与斤。

依照官方的案例演示,先下載并啟動(dòng)提供好的Java案例:

$ wget https://arthas.aliyun.com/math-game.jar
$ java -jar math-game.jar

再啟動(dòng)一個(gè)新的Terminal窗口,下載并啟動(dòng)Arthas工具:

$ wget https://arthas.aliyun.com/arthas-boot.jar
$ java -jar arthas-boot.jar

緊接著Arthas會(huì)將本機(jī)中所有的Java進(jìn)程查詢出來幽告,類似于jps/ps的作用:

[INFO] arthas-boot version: 3.5.5
[INFO] Found existing java process.......
* [1]: 161 math-game.jar

如果你的機(jī)器中啟動(dòng)了多個(gè)Java應(yīng)用梅鹦,此時(shí)會(huì)查詢出來一個(gè)應(yīng)用列表裆甩,我們可以根據(jù)前面的序號選擇自己要操作的Java應(yīng)用冗锁,如上情況中,再輸入1即可:

$ 1

最終嗤栓,Arthas成功啟動(dòng)冻河,接下來再通過Arthas提供的指令進(jìn)行操作即可:

Arthas啟動(dòng)界面

3.2、Arthas命令詳解

? ?Arthas從最初的發(fā)布開始茉帅,隨著后續(xù)社區(qū)的活躍性增強(qiáng)及用戶群體的不斷壯大叨叙,指令也越發(fā)完善與豐富,至目前為止提供了基礎(chǔ)命令堪澎、JVM命令擂错、class命令以及字節(jié)碼增強(qiáng)命令等幾大類。

3.2.1樱蛤、基礎(chǔ)命令

  • help:查看Arthas命令幫助信息钮呀。
  • cls:清空當(dāng)前屏幕中的所有信息,類似于clear命令昨凡。
  • session:查看當(dāng)前會(huì)話的信息爽醋。
  • reset:重置所有增強(qiáng)類,還原Arthas增強(qiáng)過的所有類(stop時(shí)生效)便脊。
  • version:顯示當(dāng)前的Arthas版本信息蚂四。
  • history:輸出歷史執(zhí)行過的所有命令。
  • quit:退出當(dāng)前的Arthas會(huì)話哪痰,其他會(huì)話不受影響遂赠。
  • shutdown:關(guān)閉所有Arthas會(huì)話后,退出Arthas晌杰。
  • stop:強(qiáng)制關(guān)閉Arthas并中斷所有會(huì)話解愤。
  • keymap:輸出Arthas中所有默認(rèn)的以及自定義的快捷鍵。
  • options:查看或設(shè)置Arthas的全局開關(guān)乎莉。
  • pwd:返回當(dāng)前的工作目錄位置送讲,同Linux的pwd命令。

3.2.2惋啃、類命令

  • sc:查看JVM已加載的類信息哼鬓,可選項(xiàng)如下:
    • class-pattern:類名表達(dá)式匹配(必填),如sc java.lang.String边灭。
    • -E:開啟正則表達(dá)式匹配异希,默認(rèn)為通配符匹配。
    • -c:指定class的類加載器的哈希碼绒瘦。
    • -d:顯示當(dāng)前類的詳細(xì)信息称簿,包含來源扣癣、聲明、類加載相關(guān)等信息憨降。
    • -f:輸出當(dāng)前類的屬性成員信息父虑,與-d一同使用。
    • -x:指定輸出靜態(tài)變量時(shí)屬性的遍歷深度授药,默認(rèn)為0士嚎。
    • -n:具有詳細(xì)信息的匹配類的最大數(shù)量(默認(rèn)為100)。
  • sm:查看已加載類的方法信息悔叽,可選項(xiàng)如下:
    • class-pattern:類名表達(dá)式匹配(必填)莱衩,如sm java.lang.String
    • -E:開啟正則表達(dá)式匹配娇澎,默認(rèn)為通配符匹配笨蚁。
    • -d:查看方法的詳細(xì)信息,配合方法名使用趟庄,如sm -d java.lang.String toString括细。
    • -c:同sc -c作用相同。
    • -n:同sc -h作用相同岔激。
  • jad:反編譯指定已加載類的源碼勒极,可選項(xiàng)如下:
    • -c、-E都與前面的作用相同虑鼎,舉幾個(gè)案例演示用法辱匿。
    • jad --source-only java.lang.String:只顯示反編譯后的Java源碼。
    • jad java.lang.String:反編譯指定類炫彩。
    • jad java.lang.String toString:反編譯指定類的某個(gè)方法匾七。
  • mc:內(nèi)存編譯器,編譯.java源文件為.class類文件江兢,可選項(xiàng)如下:
    • -c:指定類加載器(以哈希碼的方式指定)昨忆。
    • -d:指定編譯后的類文件輸出位置。
  • redefine:加載外部的.class文件杉允,重新加載JVM已加載的類邑贴。
    • 推薦使用retransform代替redefine
  • retransform:作用與redefine相同叔磷,熱部署的作用拢驾,用于線上替換類方法。
    • 注意點(diǎn):
      • ①重新替換JVM中被加載的類時(shí)改基,不能新增方法或?qū)傩浴?/li>
      • ②正在執(zhí)行的方法不能替換繁疤。
  • dump:導(dǎo)出已加載類的字節(jié)碼數(shù)據(jù)到指定目錄,可選項(xiàng)如下:
    • -c、-E作用與之前的相同稠腊。
    • -d:指定輸出的路徑躁染,如dump -d /usr/data/byteCode java.lang.String
  • classloader:查類加載器的繼承樹架忌,urls吞彤,類加載信息,可選項(xiàng)如下:
    • -a:顯示所有類加載器加載的所有類鳖昌。
    • -c:查看指定的類加載器的加載路徑备畦,如classloader -c 14ae5a5低飒。
    • -l:統(tǒng)計(jì)每個(gè)類加載器的加載信息许昨。
    • -r:查找某個(gè)的資源路徑,配合-c使用褥赊,如classloader -c 33909752 -r java/lang/String.class糕档。
    • -t:以樹結(jié)構(gòu)列出每個(gè)類加載器之間的父子關(guān)系。
    • -u:顯示類加載器的url統(tǒng)計(jì)信息拌喉,如加載總數(shù)速那、父子關(guān)系、加載范圍等尿背。
    • -i:查看每種類加載器的實(shí)例數(shù)量及其加載總量端仰。

3.2.3、JVM命令

  • dashboard:資源監(jiān)控儀表盤田藐,包含線程荔烧、內(nèi)存、GC汽久、運(yùn)行環(huán)境等信息鹤竭,可選項(xiàng)如下:
    • -i:刷新實(shí)時(shí)數(shù)據(jù)的間隔時(shí)間,默認(rèn)為5000ms景醇。
    • -n:刷新實(shí)時(shí)數(shù)據(jù)的次數(shù)臀稚,默認(rèn)為一直持續(xù)刷新,按ctrl+c退出三痰。
  • thread:查看當(dāng)前線程的堆棧信息吧寺,可選項(xiàng)如下:
    • -n:顯示最活躍的n條線程信息,如thread -n 5散劫。
    • -i:指定活躍性統(tǒng)計(jì)的采樣間隔時(shí)間稚机,如thread -i 5000
    • -b:自動(dòng)檢測出應(yīng)用中當(dāng)前阻塞其他線程的線程舷丹。
    • --state:查詢目前程序中處于指定狀態(tài)的線程抒钱,如thread --state BLOCKED
    • id:查看某個(gè)線程的詳細(xì)信息,如thread 21谋币。
  • jvm:查看JVM信息仗扬,包含線程/內(nèi)存/OS/內(nèi)存結(jié)構(gòu)/編譯/類加載/運(yùn)行環(huán)境等信息。
  • sysprop:查看或修改當(dāng)前JVM的系統(tǒng)屬性蕾额,如sysprop java.home早芭。
  • sysenv:,查看當(dāng)前JVM的環(huán)境參數(shù)诅蝶。
  • vmoption:查看或修改JVM的運(yùn)行時(shí)參數(shù)退个,如:
    • vmoption PrintGC:查看PrintGC是否開啟。
    • vmoption PrintGC true:更改PrintGC參數(shù)调炬。
  • logger:查看logger信息语盈,更新logger level郎哭。
  • getstatic:查看類的靜態(tài)屬性绊茧,用法:getstatic class_nmae field_name
  • ognl:執(zhí)行ognl表達(dá)式窄陡,使用方式可參考:官方指南棘钞、特殊用法缠借。
  • heapdump:類似于jmap工具的堆dump功能,使用方式:
    • heapdump /usr/data/dump/heap.hprof:導(dǎo)出堆快照到指定文件宜猜。
    • heapdump --live /usr/data/dump/heap.hprof:只導(dǎo)出存活對象的快照泼返。
  • mbean:查看Mbean的信息,詳情參考:官方文檔姨拥。
  • memory:查看JVM的內(nèi)存劃分绅喉、內(nèi)存結(jié)構(gòu)以及占用率。

3.2.4垫毙、字節(jié)碼增強(qiáng)命令

  • tt:記錄指定方法每次執(zhí)行的數(shù)據(jù)霹疫,并能在不同的時(shí)間下調(diào)用觀測,可選項(xiàng)如下:
    • <class_pattern> <method_pattern>:指定要觀測的類名+方法名综芥。
    • -t:記錄下方法每次執(zhí)行的情況丽蝎,如tt -t demo.MathGame primeFactors
    • -i <index>:查看某條執(zhí)行記錄的執(zhí)行詳情膀藐,如tt -i 1000屠阻。
    • -d <index>:刪除某條執(zhí)行記錄,配合-i使用额各,tt- d -i 1000国觉。
    • -n:設(shè)置執(zhí)行次數(shù),如tt -t -n 10 demo.MathGame primeFactors虾啦。
    • -l:顯示目前已存在的所有執(zhí)行記錄麻诀。
    • -p:重新執(zhí)行某條執(zhí)行記錄痕寓,配合-i使用,如tt -i 1001 -p蝇闭。
    • -s:通過OGNL表達(dá)式進(jìn)行查找呻率。
    • -M:指定接收結(jié)果的字節(jié)上限,默認(rèn)為1KB呻引。
    • ---replay-times:配合-p使用礼仗,指定重新執(zhí)行N次。
    • --replay-interval:執(zhí)行多次時(shí)逻悠,每次執(zhí)行時(shí)的間隔時(shí)間元践。
    • 重新執(zhí)行3次某記錄,每次間隔500mstt -i 1001 -p --replay-times 3 --replay-interval 500童谒。
  • watch:觀測指定方法的執(zhí)行情況单旁,可選項(xiàng)如下:
    • -b:在方法調(diào)用之前觀測。
    • -s:在方法成功執(zhí)行后觀測惠啄。
    • -e:在方法異常執(zhí)行后觀測慎恒。
    • -f:在方法結(jié)束后進(jìn)行觀測(默認(rèn))任内。
    • -n:指定觀測的次數(shù)撵渡。
    • 使用示例:watch -s -n 10 demo.MathGame primeFactors
  • monitor:對指定的方法執(zhí)行進(jìn)行監(jiān)控,可選項(xiàng)如下:
    • -c:指定監(jiān)控的周期死嗦,默認(rèn)為60s趋距。
    • -n:指定監(jiān)控的周期次數(shù)。
    • 使用示例:monitor -c 10 -n 3 demo.MathGame primeFactors
  • stack:輸出當(dāng)前方法被調(diào)用的調(diào)用路徑越除。
  • trace:方法內(nèi)部調(diào)用路徑节腐,并輸出方法路徑上的每個(gè)節(jié)點(diǎn)上耗時(shí),可選項(xiàng)如下:
    • -i:跳過JVM的本地方法摘盆。
    • -n:和之前的-n同義翼雀。

3.2.5、Arthas的OGNL表達(dá)式

? ?Arthas中的很多進(jìn)階操作都需要依賴于OGNL表達(dá)式進(jìn)行編寫孩擂,因此想要玩轉(zhuǎn)Arthas狼渊,自然需要對于OGNL也具備一定的基本功,接下來演示一些常規(guī)操作类垦,詳細(xì)的使用方式可參考:官方指南狈邑、特殊用法

①蚤认、調(diào)用靜態(tài)屬性

ognl '@類的全限定名@靜態(tài)屬性名'

示例:

[arthas@80573]$ ognl '@demo.MathGame@random'
②米苹、調(diào)用靜態(tài)方法

ognl '@類的全限定名@靜態(tài)方法名("參數(shù)")'

示例1:調(diào)用入?yún)榛緮?shù)據(jù)類型和集合的方法:

[arthas@80573]$ ognl '@demo.MathGame@print(100,{1,2,3,4})' -x 1
null

示例2:調(diào)用入?yún)閷ο箢愋偷姆椒ǎ?/p>

[arthas@80573]$ ognl '#obj=new java.lang.Object(),@xxx.xxx@xxx(#obj)' -x 1

示例3:調(diào)用入?yún)?code>Map類型的方法:

[arthas@80573]$ ognl '#map={"k1":"v1","k2":"v2"},@xxx.xxx@xxx(#map)' -x 1

示例4:將一個(gè)方法的執(zhí)行結(jié)果作為另一個(gè)方法的入?yún)ⅲ?/p>

[arthas@80573]$ ognl '#result=@xx.xx@A(),@xx.xx@xx(#result)' -x 1
③、調(diào)用構(gòu)造方法

ognl 'new 類的全限定名()'

示例1:調(diào)用無參創(chuàng)建對象

[arthas@80573]$ ognl 'new java.lang.Object()'

示例2:調(diào)用有參創(chuàng)建對象

[arthas@80573]$ ognl 'new xxx.xx.xxx("xx",x,{1,2,3})'

示例3:調(diào)用存在對象引用類型的構(gòu)造函數(shù)創(chuàng)建對象

[arthas@80573]$ ognl '#obj=new new java.lang.Object(),new xxx.xx.xxx(#obj)'
④砰琢、讀取不同類型的值

示例1:讀取引用對象類型的屬性值

[arthas@80573]$ ognl '@類全限定名@方法名("參數(shù)").屬性名稱'

示例2:讀取List類型的指定元素

[arthas@80573]$ ognl '@類全限定名@方法名("參數(shù)")[下標(biāo)]'

示例3:讀取Map類型的指定元素

[arthas@80573]$ ognl '@類全限定名@方法名("參數(shù)")["key"]'
⑤.........

詳細(xì)的OGNL語法可參考:官方指南蘸嘶,在線上排查時(shí)往往會(huì)結(jié)合tt良瞧、watch、monitor训唱、stack莺褒、trace等多個(gè)命令共同使用。

3.3雪情、Arthas線上常用場景

? ?Arthas中集成了大部分JDK工具的功能實(shí)現(xiàn)遵岩,因此,在線上情況時(shí)巡通,可以通過它快速的幫助我們解決問題尘执,如CPU占用過高、線程阻塞宴凉、死鎖誊锭、代碼動(dòng)態(tài)修改、方法執(zhí)行緩慢弥锄、排查404等丧靡。

3.3.1、排查CPU占用過高問題

  • ①使用thread -n 10命令查看CPU占用資源最高的10條線程籽暇。
  • ②使用thread命令查看前幾條線程的詳細(xì)執(zhí)行信息温治,定位到具體的方法。
  • ③使用monitor命令對前面定位到的方法進(jìn)行監(jiān)控戒悠,查看方法的調(diào)用次數(shù)與耗時(shí)熬荆。
  • ④分析monitor命令查詢出的結(jié)果,定位問題根源绸狐,確定是由于調(diào)用過于頻繁導(dǎo)致的卤恳,還是內(nèi)部代碼邏輯問題。
  • ⑤使用jad命令反編譯class文件寒矿,根據(jù)前面分析的原因排查代碼并改善突琳。

3.3.2、排查線程阻塞問題

  • ①使用thread查看所有線程信息符相,再篩選所有阻塞狀態(tài)的線程拆融。
  • ②根據(jù)線程名稱定位具體的業(yè)務(wù)模塊,再選中該業(yè)務(wù)中的一條線程查看堆棧信息主巍。
  • ③根據(jù)線程堆棧信息定位導(dǎo)致阻塞的具體方法冠息,再利用stack命令查看方法堆棧信息。
  • ④利用jad工具反編譯源碼孕索,分析業(yè)務(wù)邏輯代碼并改善逛艰。

3.3.3、排查死鎖問題

  • ①利用Arthas來檢測死鎖特別簡單搞旭,只需要執(zhí)行一行命令thread -b即可散怖。

3.3.4菇绵、排查方法執(zhí)行過慢問題

  • ①通過trace命令排查方法執(zhí)行速度,trace xx類 xx方法 '#cost>50ms'镇眷,觀測執(zhí)行時(shí)間大于50ms的該方法的調(diào)用信息咬最。
  • ②可以結(jié)合正則表達(dá)式,同時(shí)排查多個(gè)類欠动、多個(gè)方法永乌,trace -E ClassA|ClassB method1|method2|method3

3.3.5具伍、動(dòng)態(tài)修改線上代碼

有些項(xiàng)目編譯可能需要兩小時(shí)翅雏,好容易編譯完成上線之后,發(fā)現(xiàn)代碼有一處小地方存在邏輯錯(cuò)誤需要更改人芽,此時(shí)難度需要重新將其下線望几,重新更改后打包部署嗎?有了Arthas之后的你完全不需要這樣干萤厅。

  • ①通過jad將要修改的類反編譯為.java文件橄抹,輸出到指定目錄。
  • ②本地糾正.java文件后惕味,通過mc命令重新編譯.java文件楼誓。
  • ③通過redefineretransform命令將剛編譯的.class文件再次加載到JVM中。

這個(gè)功能是Arthas非常實(shí)用的一個(gè)功能赦拘,往往在線上環(huán)境被用于代碼糾錯(cuò)慌随、日志級別修改、Java配置文件修改等場景躺同。

3.3.6、...........

? ?顯然丸逸,Arthas還有更多的應(yīng)用場景等待你去探索蹋艺,根據(jù)不同的業(yè)務(wù)場景以及遇到的不同問題,利用Arthas都可以實(shí)現(xiàn)很好的排查與解決黄刚,上述中僅列出一些常見的應(yīng)用場景捎谨。

四、不同場景下的最佳配置推薦

? ?線上JVM的最佳參數(shù)配置往往要根據(jù)實(shí)際的業(yè)務(wù)場景以及運(yùn)行環(huán)境進(jìn)行思量憔维,首先需要弄明白業(yè)務(wù)是追求響應(yīng)速度還是吞吐量涛救,再者需要結(jié)合所部署的硬件配置及服務(wù)器環(huán)境綜合考慮,下面提供一些配置參數(shù)給予大家用作參考业扒。

4.1检吆、運(yùn)行時(shí)數(shù)據(jù)區(qū)

4.1.1、堆空間

? ?之前曾提及到程储,運(yùn)行時(shí)數(shù)據(jù)區(qū)最佳的空間大小蹭沛,以“活躍數(shù)據(jù)大小”進(jìn)行作為基礎(chǔ)參考臂寝,然后進(jìn)行設(shè)置:

根據(jù)活躍數(shù)據(jù)大小計(jì)算運(yùn)行時(shí)共享數(shù)據(jù)區(qū)大小

無論你的項(xiàng)目是追求響應(yīng)速度,亦或是吞吐量摊灭,都可根據(jù)“活躍數(shù)據(jù)”計(jì)算的大小作為基礎(chǔ)進(jìn)行調(diào)整咆贬,依照“活躍數(shù)據(jù)”計(jì)算出的大小也恰巧能夠符合Sun公司官方給出的推薦,如:

新生代空間的最佳占比應(yīng)當(dāng)在堆總大小的3/8帚呼,換算成百分比為37.5%掏缎。
通過上圖中根據(jù)“活躍數(shù)據(jù)”獲取的各分區(qū)大小進(jìn)行計(jì)算:
1200MB(Eden)/3200MB(Heap)=0.375(37.5%),和官方的推薦完全一致煤杀。

那么實(shí)際項(xiàng)目上線時(shí)御毅,“活躍數(shù)據(jù)大小”如何獲取呢?可以在測試階段進(jìn)行壓測怜珍,然后通過GC日志進(jìn)行計(jì)算端蛆。不過基于“活躍數(shù)據(jù)”計(jì)算出的大小也可以根據(jù)業(yè)務(wù)進(jìn)行調(diào)整。

  • ①對象存活較高的業(yè)務(wù)酥泛,Survivor區(qū)與Eden區(qū)比值建議為2:4今豆,即-XX:SurvivorRatio=4
  • ②對象晉升年齡閾值建議:
    • 對象存活率較低的業(yè)務(wù):保留默認(rèn)值柔袁,即15呆躲。
    • 對象存活率較高的業(yè)務(wù):建議調(diào)小,如-XX:MaxTenuringThreshold=7捶索,可以減少大量存活對象在幸存區(qū)反復(fù)橫跳帶來的性能開銷插掂。
  • ③JIT編譯的熱點(diǎn)代碼緩存區(qū)至少64M,即-XX:ReservedCodeCacheSize=64m腥例。
  • ④TLAB線程私有區(qū)域可以調(diào)整為Eden區(qū)的1/10辅甥,即-XX:TLABWasteTargetPercent=10
  • ⑤記得打開OOM時(shí)Dump堆的參數(shù)燎竖,以及執(zhí)行腳本可以指定為重啟應(yīng)用璃弄。

1.8及以上版本的JDK大多數(shù)情況下,只需要調(diào)整好每個(gè)分區(qū)的大小即可构回,其他的優(yōu)化參數(shù)夏块,大多數(shù)JVM都會(huì)默認(rèn)開啟。
-Xms纤掸、-Xmx兩參數(shù)的值需保持一致脐供,防止由于內(nèi)存動(dòng)態(tài)伸縮時(shí)造成抖動(dòng)影響性能。

4.2借跪、元空間

? ?元空間的大小建議:一般在“活躍數(shù)據(jù)”的1.2倍左右足夠政己,如果程序內(nèi)使用大量動(dòng)態(tài)代理,可以嘗試加大到1.5垦梆、1.8倍匹颤。

4.3仅孩、棧空間

? ?HotSpot中印蓖,Java虛擬機(jī)棧和本地方法棧合二為一了辽慕,因此這里的棧空間涵蓋了這兩個(gè)概念赦肃。
? ?JDK1.5之前默認(rèn)棧大小為256K溅蛉,1.5之后默認(rèn)為1M大小,對于該值的調(diào)整要基于業(yè)務(wù)來決定他宛,如果業(yè)務(wù)執(zhí)行時(shí)船侧,方法調(diào)用鏈不會(huì)太長,可以適當(dāng)縮小到512k厅各,即-Xss512K镜撩,這樣做的好處在于:在物理內(nèi)存相同的情況下,該值越小队塘,程序中就能產(chǎn)生更多的線程袁梗,從而能夠擁有更多的線程處理客戶端到來的請求。

但操作系統(tǒng)不可能允許一個(gè)進(jìn)程無限制的創(chuàng)建線程憔古,因此單個(gè)進(jìn)程中的線程數(shù)量一般最多控制3000~5000最佳遮怜。

4.2、GC垃圾收集

? ?GC方面也是JVM調(diào)優(yōu)中“操作性”最大的部分鸿市,因此锯梁,這部分在JVM調(diào)優(yōu)額外重要。

4.2.1焰情、選擇垃圾收集器

? ?選用合適的垃圾收集器往往能夠讓你的應(yīng)用性能提升一大截陌凳,但合適的收集器也需要根據(jù)運(yùn)行環(huán)境及業(yè)務(wù)場景去選擇,那如何選擇最合適的收集器呢烙样?

  • ①冯遂、將堆空間調(diào)整到合適的大小后,優(yōu)先讓JVM自行根據(jù)配置選擇谒获。
  • ②、如果內(nèi)存小于100MB或部署在單核/雙核機(jī)器壁却,使用串行收集器批狱。
  • ③、JDK8及以前追求低延遲(響應(yīng)速度)選ParNew+CMS展东,追求高吞吐則選PS+PO赔硫。
  • ④、后續(xù)新版本的JDK中盐肃,8GB以上可以考慮選用G1爪膊,上百GB規(guī)娜ㄎ颍可采用ZGC

4.2.2推盛、ParNew+CMS組合參數(shù)推薦

  • 使用ParNew+CMS組合:-XX:+UseParNewGC -XX:+UseConcMarkSweepGC峦阁。
  • ①并行收集GC線程數(shù)建議為CPU核數(shù),即-XX:ParallelCMSThreads=CPU*core耘成。
  • ②內(nèi)存碎片整理方面(MSC工作):
    • -XX:+UseCMSCompactAtFullCollection:內(nèi)存碎片化嚴(yán)重時(shí)開啟MSC整理榔昔。
    • 建議將每次FullGC后的內(nèi)存整理改為2-3輪觸發(fā)一次,即-XX:CMSFullGCsBeforeCompaction瘪菌。
  • ③因?yàn)槭亲非箜憫?yīng)速度的組合撒会,因此目標(biāo)停頓時(shí)間可以適當(dāng)偏小一些,即-XX:MaxGCPauseMillis师妙。
  • ④激進(jìn)優(yōu)化策略:
    • -XX:+CMSParallellnitialMarkEnabled:在初始階段采用多線程執(zhí)行诵肛。
    • -XX:+CMSParallelRemarkEnabled:在重新標(biāo)記階段采用多線程執(zhí)行。
    • -XX:+CMSScavengeBeforeRemark:在重新標(biāo)記階段前觸發(fā)一次新生代GC默穴。

4.2.3怔檩、ParallelScavenge+ParallelOld組合參數(shù)推薦

  • 使用PS+PO組合:-XX:+UseParallelGC -XX:+UseParallelOldGC
  • ①并行收集GC線程數(shù)建議為CPU核數(shù)壁顶,即-XX:ParallelGCThreads=CPU*core珠洗。
  • ②因?yàn)槭亲非笸掏碌慕M合,因此吞吐比盡量可以調(diào)高若专,即-XX:GCTimeRatio许蓖,如若無經(jīng)驗(yàn)沒法預(yù)估準(zhǔn)確值,那則可以開啟JVM的自適應(yīng)調(diào)整策略:-XX:+UseAdaptiveSizePolicy调衰。

4.2.4膊爪、G1整堆收集器參數(shù)推薦

  • 使用G1收集器:-XX:+UseG1GC
  • ①不要強(qiáng)制使用-Xmn參數(shù)設(shè)置年輕代的大小嚎莉,因?yàn)镚1是通過動(dòng)態(tài)調(diào)整年輕代大小達(dá)到目標(biāo)暫停時(shí)間的目的米酬。
  • ②如果分配的對象平均體積過大,可以適當(dāng)調(diào)大每個(gè)分區(qū)的Size趋箩,但必須要為2的次冪赃额,即通過-XX:G1HeapRegionSize調(diào)整,正常情況下盡量不要手動(dòng)調(diào)整叫确。
  • ③盡量可以將并發(fā)線程數(shù)調(diào)整的大一些跳芳,即-XX:ConcGCThreads,一般推薦為CPU核數(shù)+1~2竹勉。
  • ④手動(dòng)指定觸發(fā)混合GC的閾值飞盆,關(guān)閉IHOP適應(yīng)分析,消除自適應(yīng)計(jì)算的耗時(shí),-XX:InitiatingHeapOccupancyPercent=45 -XX:-G1UseAdaptiveIHOP吓歇。
  • ⑤混合GC時(shí)間過長可微調(diào)該三個(gè)參數(shù):-XX:G1MixedGCCountTarget=8 -XX:G1MixedGCLiveThresholdPercent=88 -XX:G1HeapWastePercent=5孽水。

4.3、性能激進(jìn)優(yōu)化策略

? ?在JDK1.7及其之后的版本中城看,JVM推出了很多激進(jìn)優(yōu)化的策略女气,但在1.8及其之后的環(huán)境中,大部分的參數(shù)都是默認(rèn)開啟的析命,因此我們沒有必要顯式再次開啟主卫。但其實(shí)JVM中的一些激進(jìn)優(yōu)化參數(shù)默認(rèn)也并未打開,如果你的程序堆空間足夠大鹃愤,也可以嘗試開啟后優(yōu)化程序性能簇搅。

  • -XX:ParGCCardsPerStrideChunk=4096:CMS激進(jìn)優(yōu)化策略,增大GC線程掃描卡表的范圍软吐,默認(rèn)為256瘩将,三個(gè)最佳值為32768、4K凹耙、8K姿现。
  • -XX:+AlwaysPreTouch:開啟物理內(nèi)存分配替換虛擬內(nèi)存分配,優(yōu)化分配率肖抱。
  • -XX:+UseLargePages:啟用內(nèi)存大頁面分配技術(shù)备典。
  • -XX:-UseBiasedLocking:關(guān)閉偏向鎖,在并發(fā)較高的系統(tǒng)中關(guān)閉反而可以提升性能意述。
  • -XX:AutoBoxCacheMax=20000:加大IntrgerCache的緩存提佣。
  • -XX:-UseCounterDecay:關(guān)閉JIT即時(shí)編譯器的熱度衰減機(jī)制(會(huì)消耗一定內(nèi)存)。
  • -XX:-TieredCompilation:關(guān)閉C1靜態(tài)編譯器編譯荤崇,直接使用C2編譯拌屏。
  • -XX:MaxDirectMemorySize:直接內(nèi)存大小如果確認(rèn)用的比較少,可以調(diào)小术荤,如果用的比較多倚喂,可以適當(dāng)調(diào)大。

4.4瓣戚、不同的啟動(dòng)方式參數(shù)設(shè)置方式

  • Idea/Ecalipse:在運(yùn)行時(shí)的選項(xiàng)卡中配置端圈,如IDEA的Configurations... -> VM Options中。
  • Tomcatbin目錄下的catalina.sh文件中的JAVA_OPTS的值上寫JVM參數(shù)即可子库。
  • jar包方式啟動(dòng)直接將VM參數(shù)跟在后面即可枫笛。

五、總結(jié)

? ?對于性能優(yōu)化這個(gè)內(nèi)容而言刚照,沒有絕對正確或最佳的參數(shù),也包括本章的內(nèi)容你可以適當(dāng)參考但不能照搬于生產(chǎn)環(huán)境喧兄,安全第一无畔,項(xiàng)目能夠穩(wěn)定執(zhí)行是根本啊楚,性能優(yōu)化永遠(yuǎn)要建立在應(yīng)用健康運(yùn)轉(zhuǎn)但遭遇瓶頸的基礎(chǔ)上,不要隨便調(diào)優(yōu)浑彰,更不要刻意調(diào)優(yōu)恭理。

同時(shí),對于JDK不同版本中的默認(rèn)值郭变,如果你不清楚其具體作用颜价,那建議保留默認(rèn)值,畢竟JDK默認(rèn)將其設(shè)為此值總有它的理由诉濒,默認(rèn)值至少能夠滿足絕大部分的項(xiàng)目需求周伦。因此,如若你沒有豐富的激進(jìn)優(yōu)化經(jīng)驗(yàn)未荒,再次重申:不要隨意更改一些性能參數(shù)的默認(rèn)值专挪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市片排,隨后出現(xiàn)的幾起案子寨腔,更是在濱河造成了極大的恐慌,老刑警劉巖率寡,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迫卢,死亡現(xiàn)場離奇詭異,居然都是意外死亡冶共,警方通過查閱死者的電腦和手機(jī)乾蛤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來比默,“玉大人幻捏,你說我怎么就攤上這事∶溃” “怎么了篡九?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長醋奠。 經(jīng)常有香客問我榛臼,道長,這世上最難降的妖魔是什么窜司? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任沛善,我火速辦了婚禮,結(jié)果婚禮上塞祈,老公的妹妹穿的比我還像新娘金刁。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布尤蛮。 她就那樣靜靜地躺著媳友,像睡著了一般。 火紅的嫁衣襯著肌膚如雪产捞。 梳的紋絲不亂的頭發(fā)上醇锚,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音坯临,去河邊找鬼焊唬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛看靠,可吹牛的內(nèi)容都是我干的赶促。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼衷笋,長吁一口氣:“原來是場噩夢啊……” “哼芳杏!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辟宗,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤爵赵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后泊脐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體空幻,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年容客,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秕铛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缩挑,死狀恐怖但两,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情供置,我是刑警寧澤谨湘,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站芥丧,受9級特大地震影響紧阔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜续担,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一擅耽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧物遇,春花似錦乖仇、人聲如沸憾儒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽航夺。三九已至,卻和暖如春崔涂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背始衅。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工冷蚂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汛闸。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓蝙茶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親诸老。 傳聞我的和親對象是個(gè)殘疾皇子隆夯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容