欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java應(yīng)用程序CPU100%問題排查優(yōu)化實(shí)戰(zhàn)

 更新時(shí)間:2025年02月21日 10:14:57   作者:程風(fēng)破~  
這篇文章主要介紹了如何排查和優(yōu)化Java應(yīng)用程序CPU使用率達(dá)到100%的問題,文中通過代碼示例和圖文結(jié)合的方式講解的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下

Java 應(yīng)用程序CPU 100%問題排查優(yōu)化實(shí)戰(zhàn)

今天再給大家講一個(gè) CPU 100% 優(yōu)化排查實(shí)戰(zhàn)。

收到運(yùn)維同學(xué)的報(bào)警,說某些服務(wù)器負(fù)載非常高,讓我們開發(fā)定位問題。拿到問題后先去服務(wù)器上看了看,發(fā)現(xiàn)運(yùn)行的只有我們的 Java 應(yīng)用程序。于是先用 ps 命令拿到了應(yīng)用的 PID。

ps:查看進(jìn)程的命令;PID:進(jìn)程 ID。ps -ef | grep java 可以查看所有的 Java 進(jìn)程。前面也曾講過。

接著使用 top -Hp pid 將這個(gè)進(jìn)程的線程顯示出來。輸入大寫 P 可以將線程按照 CPU 使用比例排序,于是得到以下結(jié)果。

果然,某些線程的 CPU 使用率非常高,99.9% 可不是非常高嘛(??)。

為了方便問題定位,我立馬使用 jstack pid > pid.log 將線程棧 dump 到日志文件中。關(guān)于 jstack 命令,我們前面剛剛講過。

我在上面 99.9% 的線程中隨機(jī)選了一個(gè) pid=194283 的,轉(zhuǎn)換為 16 進(jìn)制(2f6eb)后在線程快照中查詢:

在這里插入圖片描述

線程快照中線程 ID 都是16進(jìn)制的。

發(fā)現(xiàn)這是 Disruptor 的一個(gè)堆棧,好家伙,這不前面剛遇到過嘛,老熟人啊, 強(qiáng)如 Disruptor 也發(fā)生內(nèi)存溢出?

真沒想到,再來一次!

為了更加直觀的查看線程的狀態(tài),我將快照信息上傳到了專門的分析平臺(tái)上:http://fastthread.io/,估計(jì)有球友用過。

其中有一項(xiàng)展示了所有消耗 CPU 的線程,我仔細(xì)看了下,發(fā)現(xiàn)幾乎都和上面的堆棧一樣。

也就是說,都是 Disruptor 隊(duì)列的堆棧,都在執(zhí)行 java.lang.Thread.yield。

眾所周知,yield 方法會(huì)暗示當(dāng)前線程讓出 CPU 資源,讓其他線程來競爭(多線程的時(shí)候我們講過 yield,相信大家還有印象)。

根據(jù)剛才的線程快照發(fā)現(xiàn),處于 RUNNABLE 狀態(tài)并且都在執(zhí)行 yield 的線程大概有 30幾個(gè)。

初步判斷,大量線程執(zhí)行 yield 之后,在互相競爭導(dǎo)致 CPU 使用率增高,通過對(duì)堆棧的分析可以發(fā)現(xiàn),確實(shí)和 Disruptor 有關(guān)。

好家伙,又是它。

既然如此,我們來大致看一下 Disruptor 的使用方式吧。看有多少球友使用過。

第一步,在 pom.xml 文件中引入 Disruptor 的依賴:

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.4.2</version>
</dependency>

第二步,定義事件 LongEvent:

public static class LongEvent {
    private long value;

    public void set(long value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "LongEvent{value=" + value + '}';
    }
}

第三步,定義事件工廠:

// 定義事件工廠
public static class LongEventFactory implements EventFactory<LongEvent> {
    @Override
    public LongEvent newInstance() {
        return new LongEvent();
    }
}

第四步,定義事件處理器:

// 定義事件處理器
public static class LongEventHandler implements EventHandler<LongEvent> {
    @Override
    public void onEvent(LongEvent event, long sequence, boolean endOfBatch) {
        System.out.println("Event: " + event);
    }
}

第五步,定義事件發(fā)布者:

public static void main(String[] args) throws InterruptedException {
    // 指定 Ring Buffer 的大小
    int bufferSize = 1024;

    // 構(gòu)建 Disruptor
    Disruptor<LongEvent> disruptor = new Disruptor<>(
            new LongEventFactory(),
            bufferSize,
            Executors.defaultThreadFactory());

    // 連接事件處理器
    disruptor.handleEventsWith(new LongEventHandler());

    // 啟動(dòng) Disruptor
    disruptor.start();

    // 獲取 Ring Buffer
    RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();

    // 生產(chǎn)事件
    ByteBuffer bb = ByteBuffer.allocate(8);
    for (long l = 0; l < 100; l++) {
        bb.putLong(0, l);
        ringBuffer.publishEvent((event, sequence, buffer) -> event.set(buffer.getLong(0)), bb);
        Thread.sleep(1000);
    }

    // 關(guān)閉 Disruptor
    disruptor.shutdown();
}

簡單解釋下:

  • LongEvent:這是要通過 Disruptor 傳遞的數(shù)據(jù)或事件。
  • LongEventFactory:用于創(chuàng)建事件對(duì)象的工廠類。
  • LongEventHandler:事件處理器,定義了如何處理事件。
  • Disruptor 構(gòu)建:創(chuàng)建了一個(gè) Disruptor 實(shí)例,指定了事件工廠、緩沖區(qū)大小和線程工廠。
  • 事件發(fā)布:示例中演示了如何發(fā)布事件到 Ring Buffer。

大家可以運(yùn)行看一下輸出結(jié)果。

解決問題

我查了下代碼,發(fā)現(xiàn)每一個(gè)業(yè)務(wù)場景在內(nèi)部都會(huì)使用 2 個(gè) Disruptor 隊(duì)列來解耦。

假設(shè)現(xiàn)在有 7 個(gè)業(yè)務(wù),那就等于創(chuàng)建了 2*7=14 個(gè) Disruptor 隊(duì)列,同時(shí)每個(gè)隊(duì)列有一個(gè)消費(fèi)者,也就是總共有 14 個(gè)消費(fèi)者(生產(chǎn)環(huán)境更多)。

同時(shí)發(fā)現(xiàn)配置的消費(fèi)等待策略為 YieldingWaitStrategy,這種等待策略會(huì)執(zhí)行 yield 來讓出 CPU。代碼如下:

初步來看,和等待策略有很大的關(guān)系。

本地模擬

為了驗(yàn)證,我在本地創(chuàng)建了 15 個(gè) Disruptor 隊(duì)列,同時(shí)結(jié)合監(jiān)控觀察 CPU 的使用情況。

注意看代碼 YieldingWaitStrategy:

以及事件處理器:

創(chuàng)建了 15 個(gè) Disruptor 隊(duì)列,同時(shí)每個(gè)隊(duì)列都用線程池來往 Disruptor隊(duì)列 里面發(fā)送 100W 條數(shù)據(jù)。消費(fèi)程序僅僅只是打印一下。

跑了一段時(shí)間,發(fā)現(xiàn) CPU 使用率確實(shí)很高。

同時(shí) dump 線程發(fā)現(xiàn)和生產(chǎn)環(huán)境中的現(xiàn)象也是一致的:消費(fèi)線程都處于 RUNNABLE 狀態(tài),同時(shí)都在執(zhí)行 yield。

通過查詢 Disruptor 官方文檔發(fā)現(xiàn):

YieldingWaitStrategy 是一種充分壓榨 CPU 的策略,使用自旋 + yield的方式來提高性能。當(dāng)消費(fèi)線程(Event Handler threads)的數(shù)量小于 CPU 核心數(shù)時(shí)推薦使用該策略。

同時(shí)查到其他的等待策略,比如說 BlockingWaitStrategy (也是默認(rèn)的策略),使用的是鎖的機(jī)制,對(duì) CPU 的使用率不高。

于是我將等待策略調(diào)整為 BlockingWaitStrategy。

運(yùn)行后的結(jié)果如下:

和剛才的結(jié)果對(duì)比,發(fā)現(xiàn) CPU 的使用率有明顯的降低;同時(shí) dump 線程后,發(fā)現(xiàn)大部分線程都處于 waiting 狀態(tài)。

優(yōu)化解決

看樣子,將等待策略換為 BlockingWaitStrategy 可以減緩 CPU 的使用,不過我留意到官方對(duì) YieldingWaitStrategy 的描述是這樣的:
當(dāng)消費(fèi)線程(Event Handler threads)的數(shù)量小于 CPU 核心數(shù)時(shí)推薦使用該策略。

而現(xiàn)在的使用場景是,消費(fèi)線程數(shù)已經(jīng)大大的超過了核心 CPU 數(shù),因?yàn)槲业氖褂梅绞绞且粋€(gè) Disruptor 隊(duì)列一個(gè)消費(fèi)者,所以我將隊(duì)列調(diào)整為 1 個(gè)又試了試(策略依然是 YieldingWaitStrategy)。

查看運(yùn)行效果:

跑了一分鐘,發(fā)現(xiàn) CPU 的使用率一直都比較平穩(wěn)。

小結(jié)

排查到此,可以得出結(jié)論了,想要根本解決這個(gè)問題需要將我們現(xiàn)有的業(yè)務(wù)拆分;現(xiàn)在是一個(gè)應(yīng)用里同時(shí)處理了 N 個(gè)業(yè)務(wù),每個(gè)業(yè)務(wù)都會(huì)使用好幾個(gè) Disruptor 隊(duì)列。

由于在一臺(tái)服務(wù)器上運(yùn)行,所以就會(huì)導(dǎo)致 CPU 的使用率居高不下。

由于是老系統(tǒng),所以我們的調(diào)整方式如下:

先將等待策略調(diào)整為 BlockingWaitStrategy,可以有效降低 CPU 的使用率(業(yè)務(wù)上也還能接受)。第二步就需要將應(yīng)用拆分,一個(gè)應(yīng)用處理一種業(yè)務(wù)類型;然后分別部署,這樣可以互相隔離互不影響。

當(dāng)然還有一些其他的優(yōu)化,比如說這次 dump 發(fā)現(xiàn)應(yīng)用程序創(chuàng)建了 800+ 個(gè)線程。創(chuàng)建線程池的方式也是核心線程數(shù)和最大線程數(shù)一樣,就導(dǎo)致一些空閑的線程得不到回收。應(yīng)該將創(chuàng)建線程池的方式調(diào)整一下,將線程數(shù)降下來,盡量物盡其用。

好,生產(chǎn)環(huán)境中,一般也就是會(huì)遇到 OOM 和 CPU 這兩個(gè)問題,那也希望這種排查思路能夠給大家一些啟發(fā)~

以上就是Java應(yīng)用程序CPU100%問題排查優(yōu)化實(shí)戰(zhàn)的詳細(xì)內(nèi)容,更多關(guān)于Java應(yīng)用程序CPU100%的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java設(shè)計(jì)模式之原型模式詳細(xì)解析

    Java設(shè)計(jì)模式之原型模式詳細(xì)解析

    這篇文章主要介紹了Java設(shè)計(jì)模式之原型模式詳細(xì)解析,原型模式就是用一個(gè)已經(jīng)創(chuàng)建的實(shí)例作為原型,通過復(fù)制該原型對(duì)象來創(chuàng)建一個(gè)和原型對(duì)象相同的新對(duì)象,需要的朋友可以參考下
    2023-11-11
  • Spring bean為什么默認(rèn)是單例

    Spring bean為什么默認(rèn)是單例

    這篇文章主要介紹了Spring bean為什么默認(rèn)是單例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java 關(guān)系運(yùn)算符詳情及案例(上)

    Java 關(guān)系運(yùn)算符詳情及案例(上)

    這篇文章主要介紹了Java 關(guān)系運(yùn)算符詳情及案例實(shí)現(xiàn),Java 也提供了許多類型的運(yùn)算符,可以根據(jù)需要使用它們來執(zhí)行各種計(jì)算和函數(shù),包括邏輯、算術(shù)、關(guān)系等。它們根據(jù)它們提供的功能進(jìn)行分類,下面將詳細(xì)介紹該內(nèi)容,需要的朋友可以參考一下
    2021-12-12
  • Java實(shí)現(xiàn)自定義自旋鎖代碼實(shí)例

    Java實(shí)現(xiàn)自定義自旋鎖代碼實(shí)例

    這篇文章主要介紹了Java實(shí)現(xiàn)自定義自旋鎖代碼實(shí)例,Java自旋鎖是一種線程同步機(jī)制,它允許線程在獲取鎖時(shí)不立即阻塞,而是通過循環(huán)不斷嘗試獲取鎖,直到成功獲取為止,自旋鎖適用于鎖競爭激烈但持有鎖的時(shí)間很短的情況,需要的朋友可以參考下
    2023-10-10
  • Java?synchronized關(guān)鍵字性能考量及優(yōu)化探索

    Java?synchronized關(guān)鍵字性能考量及優(yōu)化探索

    這篇文章主要為大家介紹了Java?synchronized關(guān)鍵字性能考量及優(yōu)化探索示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Springboot自定義注解&傳參&簡單應(yīng)用方式

    Springboot自定義注解&傳參&簡單應(yīng)用方式

    SpringBoot框架中,通過自定義注解結(jié)合AOP可以實(shí)現(xiàn)功能如日志記錄與耗時(shí)統(tǒng)計(jì),首先創(chuàng)建LogController和TimeConsuming注解,并為LogController定義參數(shù),然后,在目標(biāo)方法上應(yīng)用這些注解,最后,使用AspectJ的AOP功能,通過切點(diǎn)表達(dá)式定位這些注解
    2024-10-10
  • Java中String、StringBuffer和StringBuilder的區(qū)別

    Java中String、StringBuffer和StringBuilder的區(qū)別

    這篇文章主要介紹了Java中String、StringBuffer和StringBuilder的區(qū)別,StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字符數(shù)組保存字符串char[]value但是沒有final關(guān)鍵字修飾,所以這兩個(gè)可變,需要的朋友可以參考下
    2024-01-01
  • Java生成圖形驗(yàn)證碼工具類

    Java生成圖形驗(yàn)證碼工具類

    這篇文章主要介紹了Java生成圖形驗(yàn)證碼工具類,本文思路明確介紹的非常詳細(xì),需要的朋友可以參考下
    2017-02-02
  • SpringBoot整合Mybatis-Plus實(shí)現(xiàn)微信注冊(cè)登錄的示例代碼

    SpringBoot整合Mybatis-Plus實(shí)現(xiàn)微信注冊(cè)登錄的示例代碼

    微信是不可或缺的通訊工具,本文主要介紹了SpringBoot整合Mybatis-Plus實(shí)現(xiàn)微信注冊(cè)登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • echarts圖表導(dǎo)出excel示例

    echarts圖表導(dǎo)出excel示例

    這篇文章主要介紹了echarts圖表導(dǎo)出excel示例,需要的朋友可以參考下
    2014-04-04

最新評(píng)論