溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

大數(shù)據(jù)中性能優(yōu)化技術(shù)指的是什么

發(fā)布時間:2022-01-05 09:32:11 來源:億速云 閱讀:176 作者:柒染 欄目:云計(jì)算

大數(shù)據(jù)中性能優(yōu)化技術(shù)指的是什么,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

1. 代碼相關(guān)

遇到性能問題,首先應(yīng)該做的是檢查否與業(yè)務(wù)代碼相關(guān)——不是通過閱讀代碼解決問題,而是通過日志或代碼,排除掉一些與業(yè)務(wù)代碼相關(guān)的低級錯誤。性能優(yōu)化的最佳位置,是應(yīng)用內(nèi)部。

譬如,查看業(yè)務(wù)日志,檢查日志內(nèi)容里是否有大量的報錯產(chǎn)生,應(yīng)用層、框架層的一些性能問題,大多數(shù)都能從日志里找到端倪(日志級別設(shè)置不合理,導(dǎo)致線上瘋狂打日志);再者,檢查代碼的主要邏輯,如 for 循環(huán)的不合理使用、NPE、正則表達(dá)式、數(shù)學(xué)計(jì)算等常見的一些問題,都可以通過簡單地修改代碼修復(fù)問題。

別動輒就把性能優(yōu)化和緩存、異步化、JVM 調(diào)優(yōu)等名詞掛鉤,復(fù)雜問題可能會有簡單解,「二八原則」在性能優(yōu)化的領(lǐng)域里里依然有效。當(dāng)然了,了解一些基本的「代碼常用踩坑點(diǎn)」,可以加速我們問題分析思路的過程,從 CPU、內(nèi)存、JVM 等分析到的一些瓶頸點(diǎn)優(yōu)化思路,也有可能在代碼這里體現(xiàn)出來。

下面是一些高頻的,容易造成性能問題的編碼要點(diǎn)。

1)正則表達(dá)式非常消耗 CPU(如貪婪模式可能會引起回溯),慎用字符串的 split()、replaceAll() 等方法;正則表達(dá)式表達(dá)式一定預(yù)編譯。

2)String.intern() 在低版本(Java 1.6 以及之前)的 JDK 上使用,可能會造成方法區(qū)(永久代)內(nèi)存溢出。在高版本 JDK 中,如果 string pool 設(shè)置太小而緩存的字符串過多,也會造成較大的性能開銷。

3)輸出異常日志的時候,如果堆棧信息是明確的,可以取消輸出詳細(xì)堆棧,異常堆棧的構(gòu)造是有成本的。注意:同一位置拋出大量重復(fù)的堆棧信息,JIT 會將其優(yōu)化后成,直接拋出一個事先編譯好的、類型匹配的異常,異常堆棧信息就看不到了。

4)避免引用類型和基礎(chǔ)類型之間無謂的拆裝箱操作,請盡量保持一致,自動裝箱發(fā)生太頻繁,會非常嚴(yán)重消耗性能。

5)Stream API 的選擇。復(fù)雜和并行操作,推薦使用 Stream API,可以簡化代碼,同時發(fā)揮來發(fā)揮出 CPU 多核的優(yōu)勢,如果是簡單操作或者 CPU 是單核,推薦使用顯式迭代。

6)根據(jù)業(yè)務(wù)場景,通過 ThreadPoolExecutor 手動創(chuàng)建線程池,結(jié)合任務(wù)的不同,指定線程數(shù)量和隊(duì)列大小,規(guī)避資源耗盡的風(fēng)險,統(tǒng)一命名后的線程也便于后續(xù)問題排查。

7)根據(jù)業(yè)務(wù)場景,合理選擇并發(fā)容器。如選擇 Map 類型的容器時,如果對數(shù)據(jù)要求有強(qiáng)一致性,可使用 Hashtable 或者 「Map + 鎖」 ;讀遠(yuǎn)大于寫,使用 CopyOnWriteArrayList;存取數(shù)據(jù)量小、對數(shù)據(jù)沒有強(qiáng)一致性的要求、變更不頻繁的,使用 ConcurrentHashMap;存取數(shù)據(jù)量大、讀寫頻繁、對數(shù)據(jù)沒有強(qiáng)一致性的要求,使用 ConcurrentSkipListMap。

8)鎖的優(yōu)化思路有:減少鎖的粒度、循環(huán)中使用鎖粗化、減少鎖的持有時間(讀寫鎖的選擇)等。同時,也考慮使用一些 JDK 優(yōu)化后的并發(fā)類,如對一致性要求不高的統(tǒng)計(jì)場景中,使用 LongAdder 替代 AtomicLong 進(jìn)行計(jì)數(shù),使用 ThreadLocalRandom 替代 Random 類等。

代碼層的優(yōu)化除了上面這些,還有很多就不一一列出了。我們可以觀察到,在這些要點(diǎn)里,有一些共性的優(yōu)化思路,是可以抽取出來的,譬如:

  1. 空間換時間:使用內(nèi)存或者磁盤,換取更寶貴的CPU 或者網(wǎng)絡(luò),如緩存的使用;

  2. 時間換空間:通過犧牲部分 CPU,節(jié)省內(nèi)存或者網(wǎng)絡(luò)資源,如把一次大的網(wǎng)絡(luò)傳輸變成多次;

  3. 其他諸如并行化、異步化、池化技術(shù)等。

2. CPU 相關(guān)

前面講到過,我們更應(yīng)該關(guān)注 CPU 負(fù)載,CPU 利用率高一般不是問題,CPU 負(fù)載 是判斷系統(tǒng)計(jì)算資源是否健康的關(guān)鍵依據(jù)。

2.1 CPU 利用率高&&平均負(fù)載高

這種情況常見于 CPU 密集型的應(yīng)用,大量的線程處于可運(yùn)行狀態(tài),I/O 很少,常見的大量消耗 CPU 資源的應(yīng)用場景有:

  1. 正則操作

  2. 數(shù)學(xué)運(yùn)算

  3. 序列化/反序列化

  4. 反射操作

  5. 死循環(huán)或者不合理的大量循環(huán)

  6. 基礎(chǔ)/第三方組件缺陷

排查高 CPU 占用的一般思路:通過 jstack 多次(> 5次)打印線程棧,一般可以定位到消耗 CPU 較多的線程堆棧?;蛘咄ㄟ^ Profiling 的方式(基于事件采樣或者埋點(diǎn)),得到應(yīng)用在一段時間內(nèi)的 on-CPU 火焰圖,也能較快定位問題。

還有一種可能的情況,此時應(yīng)用存在頻繁的 GC (包括 Young GC、Old GC、Full GC),這也會導(dǎo)致 CPU 利用率和負(fù)載都升高。排查思路:使用 jstat -gcutil 持續(xù)輸出當(dāng)前應(yīng)用的 GC 統(tǒng)計(jì)次數(shù)和時間。頻繁 GC 導(dǎo)致的負(fù)載升高,一般還伴隨著可用內(nèi)存不足,可用 free 或者 top 等命令查看下當(dāng)前機(jī)器的可用內(nèi)存大小。

CPU 利用率過高,是否有可能是 CPU 本身性能瓶頸導(dǎo)致的呢?也是有可能的??梢赃M(jìn)一步通過 vmstat 查看詳細(xì)的 CPU 利用率。用戶態(tài) CPU 利用率(us)較高,說明用戶態(tài)進(jìn)程占用了較多的 CPU,如果這個值長期大于50%,應(yīng)該著重排查應(yīng)用本身的性能問題。內(nèi)核態(tài) CPU 利用率(sy)較高,說明內(nèi)核態(tài)占用了較多的 CPU,所以應(yīng)該著重排查內(nèi)核線程或者系統(tǒng)調(diào)用的性能問題。如果 us + sy 的值大于 80%,說明 CPU 可能不足。

2.2 CPU 利用率低&&平均負(fù)載高

如果CPU利用率不高,說明我們的應(yīng)用并沒有忙于計(jì)算,而是在干其他的事。CPU 利用率低而平均負(fù)載高,常見于 I/O 密集型進(jìn)程,這很容易理解,畢竟平均負(fù)載就是 R 狀態(tài)進(jìn)程和 D 狀態(tài)進(jìn)程的和,除掉了第一種,就只剩下 D 狀態(tài)進(jìn)程了(產(chǎn)生 D 狀態(tài)的原因一般是因?yàn)樵诘却?I/O,例如磁盤 I/O、網(wǎng)絡(luò) I/O 等)。

排查&&驗(yàn)證思路:使用 vmstat 1 定時輸出系統(tǒng)資源使用,觀察 %wa(iowait) 列的值,該列標(biāo)識了磁盤 I/O 等待時間在 CPU 時間片中的百分比,如果這個值超過30%,說明磁盤 I/O 等待嚴(yán)重,這可能是大量的磁盤隨機(jī)訪問或直接的磁盤訪問(沒有使用系統(tǒng)緩存)造成的,也可能磁盤本身存在瓶頸,可以結(jié)合 iostat 或 dstat 的輸出加以驗(yàn)證,如 %wa(iowait) 升高同時觀察到磁盤的讀請求很大,說明可能是磁盤讀導(dǎo)致的問題。

此外,耗時較長的網(wǎng)絡(luò)請求(即網(wǎng)絡(luò) I/O)也會導(dǎo)致 CPU 平均負(fù)載升高,如 MySQL 慢查詢、使用 RPC 接口獲取接口數(shù)據(jù)等。這種情況的排查一般需要結(jié)合應(yīng)用本身的上下游依賴關(guān)系以及中間件埋點(diǎn)的 trace 日志,進(jìn)行綜合分析。

2.3 CPU 上下文切換次數(shù)變高

先用 vmstat 查看系統(tǒng)的上下文切換次數(shù),然后通過 pidstat 觀察進(jìn)程的自愿上下文切換(cswch)和非自愿上下文切換(nvcswch)情況。自愿上下文切換,是因?yàn)閼?yīng)用內(nèi)部線程狀態(tài)發(fā)生轉(zhuǎn)換所致,譬如調(diào)用 sleep()、join()、wait()等方法,或使用了 Lock 或 synchronized 鎖結(jié)構(gòu);非自愿上下文切換,是因?yàn)榫€程由于被分配的時間片用完或由于執(zhí)行優(yōu)先級被調(diào)度器調(diào)度所致。

如果自愿上下文切換次數(shù)較高,意味著 CPU 存在資源獲取等待,比如說,I/O、內(nèi)存等系統(tǒng)資源不足等。如果是非自愿上下文切換次數(shù)較高,可能的原因是應(yīng)用內(nèi)線程數(shù)過多,導(dǎo)致 CPU 時間片競爭激烈,頻頻被系統(tǒng)強(qiáng)制調(diào)度,此時可以結(jié)合 jstack 統(tǒng)計(jì)的線程數(shù)和線程狀態(tài)分布加以佐證。

3. 內(nèi)存相關(guān)

前面提到,內(nèi)存分為系統(tǒng)內(nèi)存和進(jìn)程內(nèi)存(含 Java 應(yīng)用進(jìn)程),一般我們遇到的內(nèi)存問題,絕大多數(shù)都會落在進(jìn)程內(nèi)存上,系統(tǒng)資源造成的瓶頸占比較小。對于 Java 進(jìn)程,它自帶的內(nèi)存管理自動化地解決了兩個問題:如何給對象分配內(nèi)存以及如何回收分配給對象的內(nèi)存,其核心是垃圾回收機(jī)制。

垃圾回收雖然可以有效地防止內(nèi)存泄露、保證內(nèi)存的有效使用,但也并不是萬能的,不合理的參數(shù)配置和代碼邏輯,依然會帶來一系列的內(nèi)存問題。此外,早期的垃圾回收器,在功能性和回收效率上也不是很好,過多的 GC 參數(shù)設(shè)置非常依賴開發(fā)人員的調(diào)優(yōu)經(jīng)驗(yàn)。比如,對于最大堆內(nèi)存的不恰當(dāng)設(shè)置,可能會引發(fā)堆溢出或者堆震蕩等一系列問題。

下面看看幾個常見的內(nèi)存問題分析思路。

3.1 系統(tǒng)內(nèi)存不足

Java 應(yīng)用一般都有單機(jī)或者集群的內(nèi)存水位監(jiān)控,如果單機(jī)的內(nèi)存利用率大于 95%,或者集群的內(nèi)存利用率大于80%,就說明可能存在潛在的內(nèi)存問題(注:這里的內(nèi)存水位是系統(tǒng)內(nèi)存)。

除了一些較極端的情況,一般系統(tǒng)內(nèi)存不足,大概率是由 Java 應(yīng)用引起的。使用 top 命令時,我們可以看到 Java 應(yīng)用進(jìn)程的實(shí)際內(nèi)存占用,其中 RES 表示進(jìn)程的常駐內(nèi)存使用,VIRT 表示進(jìn)程的虛擬內(nèi)存占用,內(nèi)存大小的關(guān)系為:VIRT > RES > Java 應(yīng)用實(shí)際使用的堆大小。除了堆內(nèi)存,Java 進(jìn)程整體的內(nèi)存占用,還有方法區(qū)/元空間、JIT 緩存等,主要組成如下:

Java 應(yīng)用內(nèi)存占用 = Heap(堆區(qū))+ Code Cache(代碼緩存區(qū)) + Metaspace(元空間)+ Symbol tables(符號表)+ Thread stacks(線程棧區(qū))+ Direct buffers(堆外內(nèi)存)+ JVM structures(其他的一些 JVM 自身占用)+ Mapped files(內(nèi)存映射文件)+ Native Libraries(本地庫)+ ...

Java 進(jìn)程的內(nèi)存占用,可以使用 jstat -gc 命令查看,輸出的指標(biāo)中可以得到當(dāng)前堆內(nèi)存各分區(qū)、元空間的使用情況。堆外內(nèi)存的統(tǒng)計(jì)和使用情況,可以利用 NMT(Native Memory Tracking,HotSpot VM Java8 引入)獲取。線程棧使用的內(nèi)存空間很容易被忽略,雖然線程棧內(nèi)存采用的是懶加載的模式,不會直接使用 +Xss 的大小來分配內(nèi)存,但是過多的線程也會導(dǎo)致不必要的內(nèi)存占用,可以使用 jstackmem 這個腳本統(tǒng)計(jì)整體的線程占用。

系統(tǒng)內(nèi)存不足的排查思路:

  1. 首先使用 free 查看當(dāng)前內(nèi)存的可用空間大小,然后使用 vmstat 查看具體的內(nèi)存使用情況及內(nèi)存增長趨勢,這個階段一般能定位占用內(nèi)存最多的進(jìn)程;

  2. 分析緩存 / 緩沖區(qū)的內(nèi)存使用。如果這個數(shù)值在一段時間變化不大,可以忽略。如果觀察到緩存 / 緩沖區(qū)的大小在持續(xù)升高,則可以使用 pcstat、cachetop、slabtop 等工具,分析緩存 / 緩沖區(qū)的具體占用;

  3. 排除掉緩存 / 緩沖區(qū)對系統(tǒng)內(nèi)存的影響后,如果發(fā)現(xiàn)內(nèi)存還在不斷增長,說明很有可能存在內(nèi)存泄漏。

3.2 Java 內(nèi)存溢出

內(nèi)存溢出是指應(yīng)用新建一個對象實(shí)例時,所需的內(nèi)存空間大于堆的可用空間。內(nèi)存溢出的種類較多,一般會在報錯日志里看到 OutOfMemoryError 關(guān)鍵字。常見內(nèi)存溢出種類及分析思路如下:

1)java.lang.OutOfMemoryError: Java heap space。原因:堆中(新生代和老年代)無法繼續(xù)分配對象了、某些對象的引用長期被持有沒有被釋放,垃圾回收器無法回收、使用了大量的 Finalizer 對象,這些對象并不在 GC 的回收周期內(nèi)等。一般堆溢出都是由于內(nèi)存泄漏引起的,如果確認(rèn)沒有內(nèi)存泄漏,可以適當(dāng)通過增大堆內(nèi)存。

2)java.lang.OutOfMemoryError:GC overhead limit exceeded。原因:垃圾回收器超過98%的時間用來垃圾回收,但回收不到2%的堆內(nèi)存,一般是因?yàn)榇嬖趦?nèi)存泄漏或堆空間過小。

3)java.lang.OutOfMemoryError: Metaspace或java.lang.OutOfMemoryError: PermGen space。排查思路:檢查是否有動態(tài)的類加載但沒有及時卸載,是否有大量的字符串常量池化,永久代/元空間是否設(shè)置過小等。

4)java.lang.OutOfMemoryError : unable to create new native Thread。原因:虛擬機(jī)在拓展??臻g時,無法申請到足夠的內(nèi)存空間。可適當(dāng)降低每個線程棧的大小以及應(yīng)用整體的線程個數(shù)。此外,系統(tǒng)里總體的進(jìn)程/線程創(chuàng)建總數(shù)也受到系統(tǒng)空閑內(nèi)存和操作系統(tǒng)的限制,請仔細(xì)檢查。

注:這種棧溢出,和 StackOverflowError 不同,后者是由于方法調(diào)用層次太深,分配的棧內(nèi)存不夠新建棧幀導(dǎo)致。此外,還有 Swap 分區(qū)溢出、本地方法棧溢出、數(shù)組分配溢出等 OutOfMemoryError 類型,由于不是很常見,就不一一介紹了。

3.3 Java 內(nèi)存泄漏

Java 內(nèi)存泄漏可以說是開發(fā)人員的噩夢,內(nèi)存泄漏與內(nèi)存溢出不同則,后者簡單粗暴,現(xiàn)場也比較好找。內(nèi)存泄漏的表現(xiàn)是:應(yīng)用運(yùn)行一段時間后,內(nèi)存利用率越來越高,響應(yīng)越來越慢,直到最終出現(xiàn)進(jìn)程「假死」。

Java 內(nèi)存泄漏可能會造成系統(tǒng)可用內(nèi)存不足、進(jìn)程假死、OOM 等,排查思路卻不外乎下面兩種:

  1. 通過 jmap 定期輸出堆內(nèi)對象統(tǒng)計(jì),定位數(shù)量和大小持續(xù)增長的對象;

  2. 使用 Profiler 工具對應(yīng)用進(jìn)行 Profiling,尋找內(nèi)存分配熱點(diǎn)。

此外,在堆內(nèi)存持續(xù)增長時,建議 dump 一份堆內(nèi)存的快照,后面可以基于快照做一些分析??煺针m然是瞬時值,但也是有一定的意義的。

3.4 垃圾回收相關(guān)

GC(垃圾回收,下同)的各項(xiàng)指標(biāo),是衡量 Java 進(jìn)程內(nèi)存使用是否健康的重要標(biāo)尺。垃圾回收最核心指標(biāo):GC Pause(包括 MinorGC 和 MajorGC) 的頻率和次數(shù),以及每次回收的內(nèi)存詳情,前者可以通過 jstat 工具直接得到,后者需要分析 GC 日志。需要注意的是,jstat 輸出列中的 FGC/FGCT 表示的是一次老年代垃圾回收中,出現(xiàn) GC Pause (即 Stop-the-World)的次數(shù),譬如對于 CMS 垃圾回收器,每次老年代垃圾回收這個值會增加2(初始標(biāo)記和重新標(biāo)記著兩個 Stop-the-World 的階段,這個統(tǒng)計(jì)值會是 2。

什么時候需要進(jìn)行 GC 調(diào)優(yōu)?這取決于應(yīng)用的具體情況,譬如對響應(yīng)時間的要求、對吞吐量的要求、系統(tǒng)資源限制等。一些經(jīng)驗(yàn):GC 頻率和耗時大幅上升、GC Pause 平均耗時超過 500ms、Full GC 執(zhí)行頻率小于1分鐘等,如果 GC 滿足上述的一些特征,說明需要進(jìn)行 GC 調(diào)優(yōu)了。

由于垃圾回收器種類繁多,針對不同的應(yīng)用,調(diào)優(yōu)策略也有所區(qū)別,因此下面介紹幾種通用的的 GC 調(diào)優(yōu)策略。

1)選擇合適的 GC 回收器。根據(jù)應(yīng)用對延遲、吞吐的要求,結(jié)合各垃圾回收器的特點(diǎn),合理選用。推薦使用 G1 替換 CMS 垃圾回收器,G1 的性能是在逐步優(yōu)化的,在 8GB 內(nèi)存及以下的機(jī)器上,其各方面的表現(xiàn)也在趕上甚至有超越之勢。G1 調(diào)參較方便,而 CMS 垃圾回收器參數(shù)太過復(fù)雜、容易造成空間碎片化、對 CPU 消耗較高等弊端,也使其目前處于廢棄狀態(tài)。Java 11 里新引入的 ZGC 垃圾回收器,基本可用做到全階段并發(fā)標(biāo)記和回收,值得期待。

2)合理的堆內(nèi)存大小設(shè)置。堆大小不要設(shè)置過大,建議不要超過系統(tǒng)內(nèi)存的 75%,避免出現(xiàn)系統(tǒng)內(nèi)存耗盡。最大堆大小和初始化堆的大小保持一致,避免堆震蕩。新生代的大小設(shè)置比較關(guān)鍵,我們調(diào)整 GC 的頻率和耗時,很多時候就是在調(diào)整新生代的大小,包括新生代和老年代的占比、新生代中 Eden 區(qū)和 Survivor 區(qū)的比例等,這些比例的設(shè)置還需要考慮各代中對象的晉升年齡,整個過程需要考慮的東西還是比較多的。如果使用 G1 垃圾回收器,新生代大小這一塊需要考慮的東西就少很多了,自適應(yīng)的策略會決定每一次的回收集合(CSet)。新生代的調(diào)整是 GC 調(diào)優(yōu)的核心,非常依賴經(jīng)驗(yàn),但是一般來說,Young GC 頻率高,意味著新生代太小(或 Eden 區(qū)和 Survivor 配置不合理),Young GC 時間長,意味著新生代過大,這兩個方向大體不差。

3)降低 Full GC 的頻率。如果出現(xiàn)了頻繁的 Full GC 或者 老年代 GC,很有可能是存在內(nèi)存泄漏,導(dǎo)致對象被長期持有,通過 dump 內(nèi)存快照進(jìn)行分析,一般能較快地定位問題。除此之外,新生代和老年代的比例不合適,導(dǎo)致對象頻頻被直接分配到老年代,也有可能會造成 Full GC,這個時候需要結(jié)合業(yè)務(wù)代碼和內(nèi)存快照綜合分析。此外,通過配置 GC 參數(shù),可以幫助我們獲取很多 GC 調(diào)優(yōu)所需的關(guān)鍵信息,如配置-XX:+PrintGCApplicationStoppedTime-XX:+PrintSafepointStatistics-XX:+PrintTenuringDistribution,分別可以獲取 GC Pause 分布、安全點(diǎn)耗時統(tǒng)計(jì)、對象晉升年齡分布的信息,加上 -XX:+PrintFlagsFinal 可以讓我們了解最終生效的 GC 參數(shù)等。

4. 磁盤I/O和網(wǎng)絡(luò)I/O

4.1 磁盤 I/O 問題排查思路:

  1. 使用工具輸出磁盤相關(guān)的輸出的指標(biāo),常用的有 %wa(iowait)、%util,根據(jù)輸判斷磁盤 I/O 是否存在異常,譬如 %util 這個指標(biāo)較高,說明有較重的 I/O 行為;

  2. 使用 pidstat 定位到具體進(jìn)程,關(guān)注下讀或?qū)懙臄?shù)據(jù)大小和速率;

  3. 使用 lsof + 進(jìn)程號,可查看該異常進(jìn)程打開的文件列表(含目錄、塊設(shè)備、動態(tài)庫、網(wǎng)絡(luò)套接字等),結(jié)合業(yè)務(wù)代碼,一般可定位到 I/O 的來源,如果需要具體分析,還可以使用 perf 等工具進(jìn)行 trace 定位 I/O 源頭。

需要注意的是,%wa(iowait)的升高不代表一定意味著磁盤 I/O 存在瓶頸,這是數(shù)值代表 CPU 上 I/O 操作的時間占用的百分比,如果應(yīng)用進(jìn)程的在這段時間內(nèi)的主要活動就是 I/O,那么也是正常的。

4.2 網(wǎng)絡(luò) I/O 存在瓶頸,可能的原因如下:

  1. 一次傳輸?shù)膶ο筮^大,可能會導(dǎo)致請求響應(yīng)慢,同時 GC 頻繁;

  2. 網(wǎng)絡(luò) I/O 模型選擇不合理,導(dǎo)致應(yīng)用整體 QPS 較低,響應(yīng)時間長;

  3. RPC 調(diào)用的線程池設(shè)置不合理??墒褂?jstack 統(tǒng)計(jì)線程數(shù)的分布,如果處于 TIMED_WAITING 或 WAITING 狀態(tài)的線程較多,則需要重點(diǎn)關(guān)注。舉例:數(shù)據(jù)庫連接池不夠用,體現(xiàn)在線程棧上就是很多線程在競爭一把連接池的鎖;

  4. RPC 調(diào)用超時時間設(shè)置不合理,造成請求失敗較多;

Java 應(yīng)用的線程堆棧快照非常有用,除了上面提到的用于排查線程池配置不合理的問題,其他的一些場景,如 CPU 飆高、應(yīng)用響應(yīng)較慢等,都可以先從線程堆棧入手。

5. 有用的一行命令

這一小節(jié)給出若干在定位性能問題的命令,用于快速定位。

1)查看系統(tǒng)當(dāng)前網(wǎng)絡(luò)連接數(shù)

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

2)查看堆內(nèi)對象的分布 Top 50(定位內(nèi)存泄漏)

jmap –histo:live $pid | sort-n -r -k2 | head-n 50

3)按照 CPU/內(nèi)存的使用情況列出前10 的進(jìn)程

#內(nèi)存
ps axo %mem,pid,euser,cmd | sort -nr | head -10
#CPU
ps -aeo pcpu,user,pid,cmd | sort -nr | head -10

4)顯示系統(tǒng)整體的 CPU利用率和閑置率

grep "cpu ">

5)按線程狀態(tài)統(tǒng)計(jì)線程數(shù)(加強(qiáng)版)

jstack $pid | grep java.lang.Thread.State:|sort|uniq -c | awk '{sum+=$1; split($0,a,":");gsub(/^[ \t]+|[ \t]+$/, "", a[2]);printf "%s: %s\n", a[2], $1}; END {printf "TOTAL: %s",sum}';

6)查看最消耗 CPU 的 Top10 線程機(jī)器堆棧信息

推薦大家使用 show-busy-java-threads 腳本,該腳本可用于快速排查 Java 的 CPU 性能問題(top us值過高),自動查出運(yùn)行的 Java 進(jìn)程中消耗 CPU 多的線程,并打印出其線程棧,從而確定導(dǎo)致性能問題的方法調(diào)用,該腳本已經(jīng)用于阿里線上運(yùn)維環(huán)境。鏈接地址:https://github.com/oldratlee/useful-scripts/。

7)火焰圖生成(需要安裝 perf、perf-map-agent、FlameGraph 這三個項(xiàng)目):

# 1. 收集應(yīng)用運(yùn)行時的堆棧和符號表信息(采樣時間30秒,每秒99個事件);
sudo perf record -F 99 -p $pid -g -- sleep 30; ./jmaps

# 2. 使用 perf script 生成分析結(jié)果,生成的 flamegraph.svg 文件就是火焰圖。
sudo perf script | ./pkgsplit-perf.pl | grep java | ./flamegraph.pl > flamegraph.svg

8)按照 Swap 分區(qū)的使用情況列出前 10 的進(jìn)程

for file in /proc/*/status ; do awk '/VmSwap|Name|^Pid/{printf $2 " " $3}END{ print ""}' $file; done | sort -k 3 -n -r | head -10

9)JVM 內(nèi)存使用及垃圾回收狀態(tài)統(tǒng)計(jì)

#顯示最后一次或當(dāng)前正在發(fā)生的垃圾收集的誘發(fā)原因
jstat -gccause $pid

#顯示各個代的容量及使用情況
jstat -gccapacity $pid

#顯示新生代容量及使用情況
jstat -gcnewcapacity $pid

#顯示老年代容量
jstat -gcoldcapacity $pid

#顯示垃圾收集信息(間隔1秒持續(xù)輸出)
jstat -gcutil $pid 1000

10)其他的一些日常命令

# 快速殺死所有的 java 進(jìn)程
ps aux | grep java | awk '{ print $2 }' | xargs kill -9

# 查找/目錄下占用磁盤空間最大的top10文件
find / -type f -print0 | xargs -0 du -h | sort -rh | head -n 10

雖然性能優(yōu)化很重要,但是不要過早在優(yōu)化上投入太多精力(當(dāng)然完善的架構(gòu)設(shè)計(jì)和編碼是必要的),過早優(yōu)化是萬惡之源。一方面,提前做的優(yōu)化工作,可能會不適用快速變化的業(yè)務(wù)需求,反倒給新需求、新功能起了阻礙的作用;另一方面,過早優(yōu)化使得應(yīng)用復(fù)雜性升高,降低了應(yīng)用的可維護(hù)性。何時進(jìn)行優(yōu)化、優(yōu)化到什么樣的程度,是一個需要多方權(quán)衡的命題。

看完上述內(nèi)容,你們掌握大數(shù)據(jù)中性能優(yōu)化技術(shù)指的是什么的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI