## 2 平均負載
平均負載是指單位時間內,系統處于可運行狀態和不可中斷狀態的平均進程數,也就是平均活躍進程數,它和CPU使用率并沒有直接關系
> 獲取平均負載
```shell
$ uptime
13:52:16 up 2 days, 16:20, 1 user, load average: 0.81, 0.75, 0.68
# 當前時間、系統運行時間以及正在登錄用戶數、過去1分鐘、5分鐘、15分鐘的平均負載(Load Average)
```
平均負載最理想的情況是等于**CPU個數**
> 判斷系統有幾個CPU
```shell
$ cat /proc/cpuinfo | grep 'model name' | wc -l
20
```
CPU 使用率,是單位時間內 CPU 繁忙情況的統計,跟平均負載并不一定完全對應:
- CPU 密集型進程,使用大量 CPU 會導致平均負載升高,此時這兩者是一致的
- I/O 密集型進程,等待 I/O 也會導致平均負載升高,但 CPU 使用率不一定很高
- 大量等待 CPU 的進程調度也會導致平均負載升高,此時的CPU使用率也會比較高
通過 iostat、mpstat、pidstat 等工具,找出平均負載升高的根源
-----
**場景一:CPU 密集型進程**
> 在第一個終端運行 stress 命令,模擬一個 CPU 使用率 100% 的場景
```shell
$ stress --cpu 1 --timeout 600
```
> 接著,在第二個終端運行 uptime,查看平均負載的變化情況
```shell
# -d 參數表示高亮顯示變化的區域
$ watch -d uptime
```
> 最后,在第三個終端運行mpstat查看 CPU 使用率的變化情況:
```shell
# -P ALL 表示監控所有CPU,后面數字5表示間隔5秒后輸出一組數據
$ mpstat -P ALL 5
```
> 使用 pidstat 來查詢哪個進程導致了 CPU 使用率升高
```shell
# 間隔5秒后輸出一組數據
$ pidstat -u 5 1
```
-----
**場景二:I/O 密集型進程**
> 運行 stress 命令,模擬 I/O 壓力,即不停地執行 sync
```shell
$ stress -i 1 --timeout 600
```
> 在第二個終端運行 uptime 查看平均負載的變化情況
```shell
$ watch -d uptime
```
> 第三個終端運行 mpstat 查看 CPU 使用率的變化情況
```shell
# 顯示所有 CPU 的指標,并在間隔 5 秒輸出一組數據
$ mpstat -P ALL 5 1
```
----
**場景三:大量進程的場景**
> 使用 stress,但這次模擬的是 25 個進程
```shell
$ stress -c 25 --timeout 600
```
```
$ uptime
```
```
$ pidstat -u 5 1
```
小結:
- 平均負載高有可能是 CPU 密集型進程導致的
- 平均負載高并不一定代表 CPU 使用率高,還有可能是 I/O 更繁忙了
- 當發現負載高的時候,可以使用 mpstat、pidstat 等工具,輔助分析負載的來源
## 3 CPU 上下文切換
根據任務的不同,CPU 的上下文切換可以分為幾個不同的場景:進程上下文切換、線程上下文切換以及中斷上下文切換
**進程上下文切換** 會重新加載 Task 的上下文到CPU寄存器和程序計數器

- 內核空間(Ring 0)具有最高權限,可以直接訪問所有資源
- 用戶空間(Ring 3)只能訪問受限資源,不能直接訪問內存等硬件設備,必須通過**系統調用**(發生兩次CPU上下文切換)陷入到內核中,才能訪問這些特權資源
- 但是系統調用過程通常稱為特權模式切換,而不是上下文切換。因為進程上下文切換,是指從一個進程切換到另一個進程運行,而系統調用過程中一直是同一個進程在運行
每次上下文切換都需要幾十納秒到數微秒的 CPU 時間([Tsuna 的測試報告](https://blog.tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html))
為了排查出現上下文切換的性能問題,需要了解**進程被調度到 CPU 上運行的時機**:
- 某個進程的 CPU 時間片耗盡了,就會被系統掛起,切換到其它正在等待 CPU 的進程運行
- 進程在系統資源不足(比如內存不足)時,要等到資源滿足后才可以運行,這個時候進程也會被掛起,并由系統調度其他進程運行
- 當進程通過睡眠函數 sleep 這樣的方法將自己主動掛起時,自然也會重新調度
- 當有優先級更高的進程運行時,為了保證高優先級進程的運行,當前進程會被掛起,由高優先級進程來運行
- 發生硬件中斷時,CPU上的進程會被中斷掛起,轉而執行內核中的中斷服務程序
----
**線程上下文切換** 會重新加載線程私有數據
線程與進程最大的區別在于,**線程是調度的基本單位,而進程則是資源擁有的基本單位**;多線程上下文切換比多進程上下文切換消耗更少的資源
----
**中斷上下文切換** 會打斷進程的正常調度和執行,轉而調度中斷處理程序,響應設備事件
對于同一個CPU,中斷處理比進程擁有更高的優先級,所以中斷上下文切換與進程上下文切換不會同時發生
中斷上下文切換也需要消耗CPU
----
小結:
- CPU 上下文切換,是保證 Linux 系統正常工作的核心功能之一,一般情況下不需要特別關注
- 但過多的上下文切換,會把CPU時間消耗在寄存器、內核棧以及虛擬內存等數據的保存和恢復上,從而縮短進程真正運行的時間,導致系統的整體性能大幅下降
----
> 通過 vmstat 獲取系統總體的上下文切換情況
```shell
$ vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 797696 10637784 2301852 11319852 0 0 4 26 3 2 1 0 98 0 0
# cs(context switch) 每秒上下文切換次數
# in(interrupt) 每秒中斷次數
# r(Running or Runable) 就緒隊列的長度,即正在運行和等待CPU的進程數
# b(Blocked) 處于不可中斷睡眠狀態的進程數
```
> 通過 pidstat 獲取每個進程的詳細情況
```shell
$ pidstat -w 5
Linux 5.10.18-amd64-desktop (swan) 2021年06月11日 _x86_64_ (20 CPU)
21時56分26秒 UID PID cswch/s nvcswch/s Command
21時56分31秒 0 1 1.59 0.60 systemd
21時56分31秒 0 12 4.37 0.00 ksoftirqd/0
21時56分31秒 0 13 63.02 0.00 rcu_sched
# cswch(voluntary context switches),每秒自愿上下文切換次數:指進程無法獲取所需資源,導致的上下文切換(例如 I/O、內存等系統資源不足時)
# nvcswch(non voluntary context switches),每秒非自愿上下文切換次數:指進程由于時間片已到等原因,被系統強制調度,進而發生的上下文切換(例如大量進程都在爭搶 CPU)
```
- 空白目錄
- 精簡版Spring的實現
- 0 前言
- 1 注冊和獲取bean
- 2 抽象工廠實例化bean
- 3 注入bean屬性
- 4 通過XML配置beanFactory
- 5 將bean注入到bean
- 6 加入應用程序上下文
- 7 JDK動態代理實現的方法攔截器
- 8 加入切入點和aspectj
- 9 自動創建AOP代理
- Redis原理
- 1 Redis簡介與構建
- 1.1 什么是Redis
- 1.2 構建Redis
- 1.3 源碼結構
- 2 Redis數據結構與對象
- 2.1 簡單動態字符串
- 2.1.1 sds的結構
- 2.1.2 sds與C字符串的區別
- 2.1.3 sds主要操作的API
- 2.2 雙向鏈表
- 2.2.1 adlist的結構
- 2.2.2 adlist和listNode的API
- 2.3 字典
- 2.3.1 字典的結構
- 2.3.2 哈希算法
- 2.3.3 解決鍵沖突
- 2.3.4 rehash
- 2.3.5 字典的API
- 2.4 跳躍表
- 2.4.1 跳躍表的結構
- 2.4.2 跳躍表的API
- 2.5 整數集合
- 2.5.1 整數集合的結構
- 2.5.2 整數集合的API
- 2.6 壓縮列表
- 2.6.1 壓縮列表的結構
- 2.6.2 壓縮列表結點的結構
- 2.6.3 連鎖更新
- 2.6.4 壓縮列表API
- 2.7 對象
- 2.7.1 類型
- 2.7.2 編碼和底層實現
- 2.7.3 字符串對象
- 2.7.4 列表對象
- 2.7.5 哈希對象
- 2.7.6 集合對象
- 2.7.7 有序集合對象
- 2.7.8 類型檢查與命令多態
- 2.7.9 內存回收
- 2.7.10 對象共享
- 2.7.11 對象空轉時長
- 3 單機數據庫的實現
- 3.1 數據庫
- 3.1.1 服務端中的數據庫
- 3.1.2 切換數據庫
- 3.1.3 數據庫鍵空間
- 3.1.4 過期鍵的處理
- 3.1.5 數據庫通知
- 3.2 RDB持久化
- 操作系統
- 2021-01-08 Linux I/O 操作
- 2021-03-01 Linux 進程控制
- 2021-03-01 Linux 進程通信
- 2021-06-11 Linux 性能優化
- 2021-06-18 性能指標
- 2022-05-05 Android 系統源碼閱讀筆記
- Java基礎
- 2020-07-18 Java 前端編譯與優化
- 2020-07-28 Java 虛擬機類加載機制
- 2020-09-11 Java 語法規則
- 2020-09-28 Java 虛擬機字節碼執行引擎
- 2020-11-09 class 文件結構
- 2020-12-08 Java 內存模型
- 2021-09-06 Java 并發包
- 代碼性能
- 2020-12-03 Java 字符串代碼性能
- 2021-01-02 ASM 運行時增強技術
- 理解Unsafe
- Java 8
- 1 行為參數化
- 1.1 行為參數化的實現原理
- 1.2 Java 8中的行為參數化
- 1.3 行為參數化 - 排序
- 1.4 行為參數化 - 線程
- 1.5 泛型實現的行為參數化
- 1.6 小結
- 2 Lambda表達式
- 2.1 Lambda表達式的組成
- 2.2 函數式接口
- 2.2.1 Predicate
- 2.2.2 Consumer
- 2.2.3 Function
- 2.2.4 函數式接口列表
- 2.3 方法引用
- 2.3.1 方法引用的類別
- 2.3.2 構造函數引用
- 2.4 復合方法
- 2.4.1 Comparator復合
- 2.4.2 Predicate復合
- 2.4.3 Function復合
- 3 流處理
- 3.1 流簡介
- 3.1.1 流的定義
- 3.1.2 流的特點
- 3.2 流操作
- 3.2.1 中間操作
- 3.2.2 終端操作
- 3.3.3 構建流
- 3.3 流API
- 3.3.1 flatMap的用法
- 3.3.2 reduce的用法
- 3.4 collect操作
- 3.4.1 collect示例
- 3.4.2 Collector接口