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

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

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

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

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

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

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

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

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

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

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

在這里插入圖片描述

線程快照中線程 ID 都是16進制的。

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

真沒想到,再來一次!

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

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

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

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

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

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

好家伙,又是它。

既然如此,我們來大致看一下 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;

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

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

    // 啟動 Disruptor
    disruptor.start();

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

    // 生產事件
    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);
    }

    // 關閉 Disruptor
    disruptor.shutdown();
}

簡單解釋下:

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

大家可以運行看一下輸出結果。

解決問題

我查了下代碼,發(fā)現每一個業(yè)務場景在內部都會使用 2 個 Disruptor 隊列來解耦。

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

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

初步來看,和等待策略有很大的關系。

本地模擬

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

注意看代碼 YieldingWaitStrategy:

以及事件處理器:

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

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

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

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

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

同時查到其他的等待策略,比如說 BlockingWaitStrategy (也是默認的策略),使用的是鎖的機制,對 CPU 的使用率不高。

于是我將等待策略調整為 BlockingWaitStrategy。

運行后的結果如下:

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

優(yōu)化解決

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

而現在的使用場景是,消費線程數已經大大的超過了核心 CPU 數,因為我的使用方式是一個 Disruptor 隊列一個消費者,所以我將隊列調整為 1 個又試了試(策略依然是 YieldingWaitStrategy)。

查看運行效果:

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

小結

排查到此,可以得出結論了,想要根本解決這個問題需要將我們現有的業(yè)務拆分;現在是一個應用里同時處理了 N 個業(yè)務,每個業(yè)務都會使用好幾個 Disruptor 隊列。

由于在一臺服務器上運行,所以就會導致 CPU 的使用率居高不下。

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

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

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

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

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

相關文章

  • Java設計模式之原型模式詳細解析

    Java設計模式之原型模式詳細解析

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

    Spring bean為什么默認是單例

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

    Java 關系運算符詳情及案例(上)

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

    Java實現自定義自旋鎖代碼實例

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

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

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

    Springboot自定義注解&傳參&簡單應用方式

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

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

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

    Java生成圖形驗證碼工具類

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

    SpringBoot整合Mybatis-Plus實現微信注冊登錄的示例代碼

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

    echarts圖表導出excel示例

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

最新評論