亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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%的資料請關(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)微信注冊登錄的示例代碼

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

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

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

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

最新評(píng)論