性能問題定位套路

前面的話

我們?cè)诠ぷ鬟^程中吧黄,肯定會(huì)遇到性能調(diào)優(yōu)及內(nèi)存溢出的問題,本篇文章會(huì)通過幾個(gè)小例子來粗略的介紹性能定位的思路及工具的使用喇肋。

性能問題分類

我們經(jīng)常遇到的服務(wù)端的性能問題一般有如下幾種:

1示弓、接口時(shí)延過高损拢,TPS不達(dá)標(biāo)
2往产、內(nèi)存溢出

栗子說明

本文栗子為使用 springboot 快速開發(fā)了兩個(gè) http 接口被碗,一個(gè)是列表排序栗子,模擬耗時(shí)操作仿村,一個(gè)是往一個(gè)全局列表中不停的插入數(shù)據(jù)達(dá)到內(nèi)存溢出的效果锐朴。
關(guān)于列表排序,這里使用兩種排序方式蔼囊,一種是簡(jiǎn)單的冒泡排序包颁,一種是 jdk 里列表的排序方式:加強(qiáng)型多路歸并排序,用兩個(gè)排序算法主要為了說明 JHM 的使用方式压真。

TPS 不達(dá)標(biāo)問題分析

對(duì)于此類問題,則一般是在性能測(cè)試階段就能發(fā)現(xiàn)蘑险。此時(shí)調(diào)優(yōu)一般在性能測(cè)試環(huán)境上進(jìn)行滴肿。
如何找出耗時(shí)操作呢,JDK 已經(jīng)給我們提供了一系列的工具來定位該問題了佃迄,這里我們使用Java VisualVM來診斷接口性能泼差。
首先在啟動(dòng)腳本里打開 JVM 的 JMX 端口贵少,打開方式為-Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=10.234.196.199
啟動(dòng)之后,我們就可以通過Java VisualVM來監(jiān)控我們的 JVM 了堆缘。
如圖:

性能監(jiān)控1.png

打開抽樣器滔灶,進(jìn)行 CPU 抽樣,統(tǒng)計(jì)各個(gè)接口消耗 CPU 時(shí)間吼肥。
使用壓測(cè)工具录平,持續(xù)的壓測(cè)有性能問題的接口。這里使用 jmeter 進(jìn)行壓測(cè)缀皱。壓測(cè)一段時(shí)間后斗这,打印 CPU 快照,如圖:


性能監(jiān)控2.png

這里發(fā)現(xiàn)我們?cè)谡{(diào)用 getPerf 接口時(shí)啤斗,進(jìn)一步調(diào)用了 process1 接口表箭,這個(gè)接口里有 bubbleSort 方法和 jdk 自帶的 sort 兩個(gè)調(diào)用。這兩個(gè)都是對(duì)列表排序钮莲,發(fā)現(xiàn)大部分時(shí)間都耗在冒泡排序上免钻。這里 bubbleSort 就是需要優(yōu)化的地方。排序算法有很多崔拥,不同的數(shù)據(jù)量极舔,不同的排序方法耗時(shí)也不一樣。這里需要用 JMH 來評(píng)估算法的耗時(shí)握童。
JMH 相關(guān)介紹可以參考JMH,這里有相關(guān)的例子可以參考

一般對(duì)于耗時(shí)操作的優(yōu)化姆怪,可以有如下方式:
1、優(yōu)化自身算法澡绩,降低算法的時(shí)間復(fù)雜度
2稽揭、同步操作異步化。
對(duì)于異步化操作肥卡,又有如下方式:

1溪掀、異步線程
2、線程池步鉴,線程復(fù)用(線程池的大小如何確定揪胃,CPU 密集型和 IO 密集型)
3、發(fā)布訂閱(消息隊(duì)列或者 spring 的 event 機(jī)制)

3氛琢、使用緩存機(jī)制【多級(jí)緩存喊递,問題:緩存一致性,緩存防并發(fā)阳似,防雪崩----一個(gè)大專題】
4骚勘、業(yè)務(wù)流程上進(jìn)行優(yōu)化,提供專門的接口,只做當(dāng)前業(yè)務(wù)俏讹,不考慮復(fù)用性当宴。
5、如果是數(shù)據(jù)庫(kù)查詢慢泽疆,則需要優(yōu)化數(shù)據(jù)庫(kù)【這又是一個(gè)大專題】户矢。sql 優(yōu)化?殉疼?梯浪? 表優(yōu)化,如果有聯(lián)表查詢株依,則可以考慮不滿足 3 范式驱证,拉平表結(jié)構(gòu)。

如果無(wú)法在測(cè)試環(huán)境上復(fù)現(xiàn)恋腕,則可以試用 arthas 工具抹锄,attach 到相關(guān)進(jìn)程,通過 arthas 命令大致查看每個(gè)請(qǐng)求的耗時(shí)荠藤。關(guān)于 arthas 的用法伙单,可以參考arthas

內(nèi)存溢出問題分析

為什么內(nèi)存溢出會(huì)出現(xiàn)接口時(shí)延過高呢?
我們服務(wù)端一般是 JAVA 語(yǔ)言開發(fā)哈肖,如果 JVM 虛擬機(jī)內(nèi)存不足時(shí)吻育,會(huì)觸發(fā) FullGC,F(xiàn)ullGC 會(huì)吃大量的 CPU 時(shí)間淤井。如果我們的內(nèi)存一直不足布疼,頻繁的 GC,則會(huì) STW币狠,CPU 居高不下游两,留給業(yè)務(wù)的 CPU 時(shí)間就降低,導(dǎo)致業(yè)務(wù)接口時(shí)延上升漩绵。
內(nèi)存溢出的例子代碼如下

 public void process2() {
        String name = "The Spring Framework provides a comprehensive programming and configuration model for" +
                "modern Java-based enterprise applications - on any kind of deployment platform" +
                "A key element of Spring is infrastructural support at the application level: Spring focuses on the" +
                "Complete set of java.time based setters on HttpHeaders, CacheControl, CorsConfiguration.\n" +
                "@RequestMapping has enhanced produces condition support such that if a media type is declared with a specific parameter, and the requested media types (e.g. from \"Accept\" header) also has that parameter, the parameter values must match. This can be used for example to differentiate methods producing ATOM feeds \"application/atom+xml;type=feed\" vs ATOM entries \"application/atom+xml;type=entry\".\n" +
                "CORS revision that adds Vary header for non CORS requests on CORS enabled endpoints and avoid considering same-origin requests with an Origin header as a CORS request.\n" +
                "Upgrade to Jackson 2.10\n" +
                "Spring Web MVC\n" +
                "New \"WebMvc.fn\" programming model, analogous to the existing \"WebFlux.fn\":\n" +
                "A functional alternative to annotated controllers built on the Servlet API.\n" +
                "WebMvc.fn Kotlin DSL.\n" +
                "Request mapping performance optimizations through caching of the lookup path per HandlerMapping, and pre-computing frequently used data in RequestCondition implementations.\n" +
                "Improved, compact logging of request mappings on startup.\n" +
                "Spring WebFlux\n" +
                "Refinements to WebClient API to make the retrieve() method useful for most common cases, specifically adding the ability to retrieve status and headers and addition to the body. The exchange() method is only for genuinely advanced cases, and when using it, applications can now rely on ClientResponse#createException to simplify selective handling of exceptions.\n" +
                "Support for Kotlin Coroutines.\n" +
                "Server and client now use Reactor checkpoints to insert information about the request URL being processed,sce or the handler used, that is then inserted into exceptions and logged below the exception stacktrace.\n" +
                "Request mapping performance optimizations through pre-computing frequently used data in RequestCondition implementations.\n" +
                "Header management performance optimizations by wrapping rather than copying server headers, and caching parsed representations of media types. Available from 5.1.1, see issue #21783 and commits under \"Issue Links\".\n" +
                "Improved, compact logging of request mappings on startup.\n" +
                "Add ServerWebExchangeContextFilter to expose the Reactor Context as an exchange attribute.\n" +
                "Add FreeMarker macros support.\n" +
                "MultipartBodyBuilder improvements to allow Publisher and Part as input along with option to specify the filename to use for a part.";
        list.add(name + System.currentTimeMillis());

    }

這里往一個(gè)全局的 list 中添加一個(gè)字符串贱案,每次請(qǐng)求時(shí),添加一個(gè)字符串止吐。
-Xms200m -Xmx200m這里把 jvm 堆內(nèi)存大小設(shè)置為 200m宝踪。
對(duì)于內(nèi)存溢出,則需要 gc log 和內(nèi)存快照碍扔。gc log 可以在https://gceasy.io上面分析瘩燥,可以看到相關(guān)的fullgc和yong gc 的情況。gc 分析如圖:

gc分析.png

該圖表明發(fā)生 GC 之后不同,對(duì)大小并沒有明顯的減少厉膀,可能是堆內(nèi)存不太夠用。圖左邊的每個(gè)按鈕對(duì)應(yīng)一個(gè)分析。

定位出內(nèi)存不足后站蝠,就要看內(nèi)存中哪些對(duì)象回收不掉,這時(shí)需要使用到 jmap 命令卓鹿,dump 出內(nèi)存快照菱魔。命令如下:
jmap -dump:format=b,file=heapdump.hprof pid?,
獲取到內(nèi)存快照可以使用 mat 進(jìn)行分析。
使用 mat 打開快照文件吟孙,如下:

mat_preview.png

這里看到最大一塊內(nèi)存是 81.7M,點(diǎn)擊餅圖進(jìn)入如下頁(yè)面:


list.png

上圖可以看到在類 businessServiceImpl 中有個(gè) list澜倦,該 list 共有 16081 個(gè)元素,每個(gè)元素大小 5296 個(gè)字節(jié)杰妓。共有 82M藻治。
點(diǎn)擊 value,可以查看 list 中具體的值巷挥。如圖:


list_value_detail.png

發(fā)現(xiàn)正是我們代碼里插入的字符串桩卵。

CPU 高

使用 top 命令,查看哪個(gè)進(jìn)程 CPU 高倍宾,通過 top -p <PID> -H 查看哪個(gè)線程消耗 CPU雏节。使用 jstack 命令打印出 java 進(jìn)程的線程堆棧,通過線程號(hào)找到相應(yīng)的 java 線程高职,結(jié)合 java 代碼钩乍,一般可以找出系統(tǒng)的耗 CPU 代碼。
相關(guān)操作可以參考如下文章:
誰(shuí)偷走了你的服務(wù)器性能

寫在最后

這里只是通過一些栗子說明了性能工具的使用方法怔锌,只是一個(gè)引子寥粹,隨后會(huì)進(jìn)一步介紹如何進(jìn)行性能的優(yōu)化。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末埃元,一起剝皮案震驚了整個(gè)濱河市涝涤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亚情,老刑警劉巖妄痪,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異楞件,居然都是意外死亡衫生,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門土浸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罪针,“玉大人,你說我怎么就攤上這事黄伊±峤矗” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)墓阀。 經(jīng)常有香客問我毡惜,道長(zhǎng),這世上最難降的妖魔是什么斯撮? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任经伙,我火速辦了婚禮,結(jié)果婚禮上勿锅,老公的妹妹穿的比我還像新娘帕膜。我一直安慰自己,他們只是感情好溢十,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布垮刹。 她就那樣靜靜地躺著,像睡著了一般张弛。 火紅的嫁衣襯著肌膚如雪荒典。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天乌庶,我揣著相機(jī)與錄音种蝶,去河邊找鬼。 笑死瞒大,一個(gè)胖子當(dāng)著我的面吹牛螃征,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播透敌,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼盯滚,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了酗电?” 一聲冷哼從身側(cè)響起魄藕,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎撵术,沒想到半個(gè)月后背率,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嫩与,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年寝姿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片划滋。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饵筑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出处坪,到底是詐尸還是另有隱情根资,我是刑警寧澤架专,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站玄帕,受9級(jí)特大地震影響部脚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜裤纹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一睛低、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧服傍,春花似錦、人聲如沸骂铁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拉庵。三九已至灿椅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钞支,已是汗流浹背茫蛹。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烁挟,地道東北人婴洼。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像撼嗓,于是被迫代替她去往敵國(guó)和親柬采。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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

  • 第二部分 自動(dòng)內(nèi)存管理機(jī)制 第二章 java內(nèi)存異常與內(nèi)存溢出異常 運(yùn)行數(shù)據(jù)區(qū)域 程序計(jì)數(shù)器:當(dāng)前線程所執(zhí)行的字節(jié)...
    小明oh閱讀 1,141評(píng)論 0 2
  • http://www.cnblogs.com/angeldevil/p/3801189.html值得一看 Clas...
    snail_knight閱讀 1,418評(píng)論 1 0
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理且警,因此不免有一些不準(zhǔn)確的地方粉捻,同時(shí)不同JDK版本的...
    高廣超閱讀 15,570評(píng)論 3 83
  • Java類別問題 1. String與StringBuilder、StringBuffer的區(qū)別 如果要操作少量...
    梁小中閱讀 356評(píng)論 0 4
  • 最近可以說是很喪了斑芜,無(wú)奈牙齒過敏一月未好只得去看肩刃,誰(shuí)知五分鐘完事被愉悅地告知牙齒很健康也很干凈堅(jiān)持使用抗過敏牙膏,...
    陶一_閱讀 322評(píng)論 0 0