最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

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

收到運(yùn)維同學(xué)的報(bào)警,說(shuō)某些服務(wù)器負(fù)載非常高,讓我們開(kāi)發(fā)定位問(wèn)題。拿到問(wèn)題后先去服務(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)程。前面也曾講過(guò)。

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

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

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

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

在這里插入圖片描述

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

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

真沒(méi)想到,再來(lái)一次!

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

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

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

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

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

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

好家伙,又是它。

既然如此,我們來(lái)大致看一下 Disruptor 的使用方式吧。看有多少球友使用過(guò)。

第一步,在 pom.xml 文件中引入 Disruptor 的依賴(lài):

<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();
}

簡(jiǎn)單解釋下:

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

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

解決問(wèn)題

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

假設(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 來(lái)讓出 CPU。代碼如下:

初步來(lái)看,和等待策略有很大的關(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ì)列都用線程池來(lái)往 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。

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

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

同時(shí)查到其他的等待策略,比如說(shuō) 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 的使用,不過(guò)我留意到官方對(duì) YieldingWaitStrategy 的描述是這樣的:
當(dāng)消費(fèi)線程(Event Handler threads)的數(shù)量小于 CPU 核心數(shù)時(shí)推薦使用該策略。

而現(xiàn)在的使用場(chǎng)景是,消費(fèi)線程數(shù)已經(jīng)大大的超過(guò)了核心 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è)問(wèn)題需要將我們現(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ù)類(lèi)型;然后分別部署,這樣可以互相隔離互不影響。

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

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

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

相關(guān)文章

最新評(píng)論

邵阳县| 旬阳县| 秀山| 阳信县| 郴州市| 吴旗县| 泗洪县| 竹溪县| 博乐市| 岑溪市| 江津市| 邵阳市| 株洲市| 兴海县| 报价| 黔东| 东兴市| 林州市| 邳州市| 调兵山市| 怀集县| 浦城县| 洮南市| 隆德县| 红原县| 澄迈县| 崇礼县| 双牌县| 汤原县| 富宁县| 揭西县| 桂林市| 霸州市| 永德县| 哈尔滨市| 建湖县| 泌阳县| 清河县| 大冶市| 都江堰市| 齐齐哈尔市|