成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一次線上JVM GC 長暫停排查,加班搞了好久

開發 前端
JVM進行GC時,需要對對應堆分區的已用內存進行遍歷,假如GC的時候,有堆的一部分內容被交換到swap中,遍歷到這部分的時候就須要將其交換回內存;更極端情況同一時刻因為內存空間不足,就需要把內存中堆的另外一部分換到SWAP中去,于是在遍歷堆分區的過程中,會把整個堆分區輪流往SWAP寫一遍,導致GC時間超長。

背景

在高并發下,Java程序的GC問題屬于很典型的一類問題,帶來的影響往往會被進一步放大。不管是「GC頻率過快」還是「GC耗時太長」,由于GC期間都存在Stop The World問題,因此很容易導致服務超時,引發性能問題。

事情最初是線上某應用垃圾收集出現Full GC異常的現象,應用中個別實例Full GC時間特別長,持續時間約為15~30秒,平均每2周左右觸發一次;

圖片圖片

圖片圖片

JVM參數配置:

-Xms2048M –Xmx2048M –Xmn1024M –XX:MaxPermSize=512M

圖片圖片

排查過程

分析 GC 日志

GC 日志它記錄了每一次的 GC 的執行時間和執行結果,通過分析 GC 日志可以調優堆設置和 GC 設置,或者改進應用程序的對象分配模式。

這里Full GC的reason是Ergonomics,是因為開啟了UseAdaptiveSizePolicy,jvm自己進行自適應調整引發的Full GC。

這份日志主要體現GC前后的變化,目前為止看不出個所以然來。

圖片圖片

開啟GC日志,需要添加如下 JVM 啟動參數:

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/export/log/risk_pillar/gc.log

常見的 Young GC、Full GC 日志含義如下:

圖片圖片

進一步查看服務器性能指標

獲取到了GC耗時的時間后,通過監控平臺獲取到各個監控項,開始排查這個時點有異常的指標,最終分析發現,在5.06分左右(GC的時點),CPU占用顯著提升,而SWAP出現了釋放資源、memory資源增長出現拐點的情況(詳見下圖紅色框,橙色框中的變化是因修改配置導致,后面會介紹,暫且可忽略)

圖片圖片

JVM用到了swap?

是因為GC導致的CPU突然飆升,并且釋放了swap交換區這部分內存到memory?

為了驗證JVM是否用到swap,我們通過檢查proc下的進程內存資源占用情況

for i in (cd/proc;ls∣grep"[0?9]"∣awk′0 >100');
do awk '/Swap:/{a=a+2}END{print '"i"',a/1024"M"}' /proc/$i/smaps 2>/dev/null;
done | sort -k2nr | head -10 

# head -10 表示 取出 前10個內存占用高的進程 
# 取出的第一列為進程的id 第二列進程占用swap大小

看到確實有用到305MB的swap

圖片圖片

這里簡單介紹下什么是swap?

swap指的是一個交換分區或文件,主要是在內存使用存在壓力時,觸發內存回收,這時可能會將部分內存的數據交換到swap空間,以便讓系統不會因為內存不夠用而導致oom或者更致命的情況出現。

當某進程向OS請求內存發現不足時,OS會把內存中暫時不用的數據交換出去,放在swap分區中,這個過程稱為swap out。

當某進程又需要這些數據且OS發現還有空閑物理內存時,又會把swap分區中的數據交換回物理內存中,這個過程稱為swap in。

為了驗證GC耗時與swap操作有必然關系,我抽查了十幾臺機器,重點關注耗時長的GC日志,通過時間點確認到GC耗時的時間點與swap操作的時間點確實是一致的。

進一步查看虛擬機各實例 swappiness 參數,一個普遍現象是,凡是發生較長Full GC的實例都配置了參數 vm.swappiness = 30(值越大表示越傾向于使用swap);而GC時間相對正常的實例配置參數 vm.swappiness = 0(最大限度地降低使用swap)。

swappiness 可以設置為 0 到 100 之間的值,它是Linux的一個內核參數,控制系統在進 行swap時,內存使用的相對權重。

  • swappiness=0: 表示最大限度使用物理內存,然后才是 swap空間
  • swappiness=100: 表示積極的使用swap分區,并且把內存上的數據及時的交換到swap空間里面

圖片圖片

圖片圖片

對應的物理內存使用率和swap使用情況如下

圖片圖片

圖片圖片

至此,矛頭似乎都指向了swap。

問題分析

當內存使用率達到水位線(vm.swappiness)時,linux會把一部分暫時不使用的內存數據放到磁盤swap去,以便騰出更多可用內存空間;

當需要使用位于swap區的數據時,再將其換回內存中,當JVM進行GC時,需要對相應堆分區的已用內存進行遍歷;

假如GC的時候,有堆的一部分內容被交換到swap空間中,遍歷到這部分的時候就需要將其交換回內存,由于需要訪問磁盤,所以相比物理內存,它的速度肯定慢的令人發指,GC停頓的時間一定會非常非常恐怖;

進而導致Linux對swap分區的回收滯后(內存到磁盤換入換出操作十分占用CPU與系統IO),在高并發/QPS服務中,這種滯后帶來的結果是致命的(STW)。

問題解決

至此,答案似乎很清晰,我們只需嘗試把swap關閉或釋放掉,看看能否解決問題?

如何釋放swap?

設置vm.swappiness=0(重啟應用釋放swap后生效),表示盡可能不使用交換內存

方案 a:臨時設置方案,重啟后不生效

  1. 設置vm.swappiness為0,sysctl vm.swappiness=0
  2. 查看swappiness值,cat /proc/sys/vm/swappiness

方案b:永久設置方案,重啟后仍然生效

  1. vi /etc/sysctl.conf
  2. 關閉交換分區swapoff –a(前提:首先要保證內存剩余要大于等于swap使用量,否則會報Cannot allocate memory!swap分區一旦釋放,所有存放在swap分區的文件都會轉存到物理內存上,可能會引發系統IO或者其他問題。)

查看當前swap分區掛載在哪:

圖片圖片

關停分區:

圖片圖片

關閉swap交換區后的內存變化見下圖橙色框,此時swap分區的文件都轉存到了物理內存上

圖片圖片

關閉Swap交換區后,于2.23再次發生Full GC,耗時190ms,問題得到解決。

圖片圖片

疑惑

  1. 是不是只要開啟了swap交換區的JVM,在GC的時候都會耗時較長呢?
  2. 既然JVM對swap如此不待見,為何JVM不明令禁止使用呢?
  3. swap工作機制是怎樣的?這臺物理內存為8g的server,使用了交換區內存(swap),說明物理內存不夠使用了,但是通過free命令查看內存使用情況,實際物理內存似乎并沒有占用那么多,反而Swap已占近1G?

圖片圖片

free:除了buff/cache剩余了多少內存

shared:共享內存

buff/cache:緩沖、緩存區內存數(使用過高通常是程序頻繁存取文件)

available:真實剩余的可用內存數

進一步思考

大家可以想想,關閉交換磁盤緩存意味著什么?

其實大可不必如此激進,要知道這個世界永遠不是非0即1的,大家都會或多或少選擇走在中間,不過有些偏向0,有些偏向1而已。

很顯然,在swap這個問題上,JVM可以選擇偏向盡量少用,從而降低swap影響,要降低swap影響有必要弄清楚Linux內存回收是怎么工作的,這樣才能不遺漏任何可能的疑點。

先來看看swap是如何觸發的?

Linux會在兩種場景下觸發內存回收,一種是在內存分配時發現沒有足夠空閑內存時會立刻觸發內存回收;另一種是開啟了一個守護進程(kswapd進程)周期性對系統內存進行檢查,在可用內存降低到特定閾值之后主動觸發內存回收。

通過如下圖示可以很容易理解,詳細信息參見:http://hbasefly.com/2017/05/24/hbase-linux/

圖片

是不是只要開啟了swap交換區的JVM,在GC的時候都會耗時較長?

筆者去查了一下另外的一個應用,相關指標信息請見下圖。

實名服務的QPS是非常高的,同樣能看到應用了swap,GC平均耗時 576ms,這是為什么呢?

圖片圖片

圖片圖片

通過把時間范圍聚焦到發生GC的某一時間段,從監控指標圖可以看到swapUsed沒有任何變化,也就是說沒有swap活動,進而沒有影響到垃級回收的總耗時。

圖片圖片

圖片圖片

通過如下命令列舉出各進程swap空間占用情況,很清楚的看到實名這個服務swap空間占用的較少(僅54.2MB)

圖片圖片

另一個顯著的現象是實名服務Full GC間隔較短(幾個小時一次),而我的服務平均間隔2周一次Full GC

圖片圖片

圖片圖片

基于以上推測

  1. 實名服務由于 GC 間隔較短,內存中的東西根本沒有機會置換到swap中就被回收了,GC的時候不需要將swap分區中的數據交換回物理內存中,完全基于內存計算,所以要快很多
  2. 將哪些內存數據置換進swap交換區的篩選策略應該是類似于LRU算法(最近最少使用原則)

為了證實上述猜測,我們只需跟蹤swap變更日志,監控數據變化即可得到答案,這里采用一段shell 腳本實現

#!/bin/bash 
echo -e `date +%y%m%d%H%M%S` 
echo -e "PID\t\tSwap\t\tProc_Name" 

#拿出/proc目錄下所有以數字為名的目錄(進程名是數字才是進程,其他如sys,net等存放的是其他信息) 
for pid in `ls -l /proc | grep ^d | awk '{ print $9 }'| grep -v [^0-9]` 
do 
    if [ $pid -eq 1 ];then continue;fi 
    grep -q "Swap" /proc/$pid/smaps 2>/dev/null 
    if [ $? -eq 0 ];then 
        swap=$(gawk '/Swap/{ sum+=$2;} END{ print sum }' /proc/$pid/smaps) #統計占用的swap分區的 大小 單位是KB 
        proc_name=$(ps aux | grep -w "$pid" | awk '!/grep/{ for(i=11;i<=NF;i++){ printf("%s ",$i); }}') #取出進程的名字 
        if [ $swap -gt 0 ];then #判斷是否占用swap 只有占用才會輸出 
            echo -e "${pid}\t${swap}\t${proc_name:0:100}" 
    fi 
   fi
done | sort -k2nr | head -10 | gawk -F'\t' '{ #排序取前 10 
    pid[NR]=$1; 
    size[NR]=$2; 
    name[NR]=$3; 
} 
END{ 
    for(id=1;id<=length(pid);id++) 
    { 
    if(size[id]<1024) 
        printf("%-10s\t%15sKB\t%s\n",pid[id],size[id],name[id]); 
    else if(size[id]<1048576) 
        printf("%-10s\t%15.2fMB\t%s\n",pid[id],size[id]/1024,name[id]);
    else 
    printf("%-10s\t%15.2fGB\t%s\n",pid[id],size[id]/1048576,name[id]); 
    } 
}

由于上面圖中 2022.3.2 19:57:00 至 2022.3.2 19:58:00 發生了一次Full GC,我們重點關注下這一分鐘內swap交換區的變化即可,我這里每10s做一次信息采集,可以看到在GC時點前后,swap確實沒有變化

圖片圖片

通過上述分析,回歸本文核心問題上,現在看來我的處理方式過于激進了,其實也可以不用關閉swap,通過適當降低堆大小,也是能夠解決問題的。

這也側面的說明,部署Java服務的Linux系統,在內存分配上并不是無腦大而全,需要綜合考慮不同場景下JVM對Java永久代 、Java堆(新生代和老年代)、線程棧、Java NIO所使用內存的需求。

總結

綜上,我們得出結論,swap和GC同一時候發生會導致GC時間非常長,JVM嚴重卡頓,極端的情況下會導致服務崩潰。

主要原因是:JVM進行GC時,需要對對應堆分區的已用內存進行遍歷,假如GC的時候,有堆的一部分內容被交換到swap中,遍歷到這部分的時候就須要將其交換回內存;更極端情況同一時刻因為內存空間不足,就需要把內存中堆的另外一部分換到SWAP中去,于是在遍歷堆分區的過程中,會把整個堆分區輪流往SWAP寫一遍,導致GC時間超長。線上應該限制swap區的大小,如果swap占用比例較高應該進行排查和解決,適當的時候可以通過降低堆大小,或者添加物理內存。

因此,部署Java服務的Linux系統,在內存分配上要慎重。

責任編輯:武曉燕 來源: JAVA日知錄
相關推薦

2022-12-17 19:49:37

GCJVM故障

2021-05-13 08:51:20

GC問題排查

2019-09-10 10:31:10

JVM排查解決

2023-01-04 18:32:31

線上服務代碼

2021-11-23 21:21:07

線上排查服務

2022-08-01 20:29:48

分布式架構數據

2020-08-27 21:36:50

JVM內存泄漏

2021-05-31 10:08:44

工具腳本主機

2019-04-15 13:15:12

數據庫MySQL死鎖

2025-03-17 10:01:07

2023-04-06 07:53:56

Redis連接問題K8s

2025-03-24 08:51:16

2024-10-10 15:32:51

2019-06-24 08:17:55

CPUFullGCJava

2017-12-19 14:00:16

數據庫MySQL死鎖排查

2019-03-15 16:20:45

MySQL死鎖排查命令

2025-03-11 08:48:35

JVMOOM事故

2021-03-29 12:35:04

Kubernetes環境TCP

2022-11-03 16:10:29

groovyfullGC

2019-01-21 11:17:13

CPU優化定位
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲国产成人精品女人久久久 | 国产精品精品久久久 | 狠狠爱网址 | 成人在线视频一区二区三区 | 欧美11一13sex性hd | 欧美一二三 | 亚洲精久| 黑人精品欧美一区二区蜜桃 | 成人久久18免费网站图片 | 成人免费一区二区三区视频网站 | 天天天天天天操 | 看特级黄色片 | 不卡一区二区三区四区 | 福利影院在线看 | 99精品国产一区二区青青牛奶 | 性色av一区 | 欧美一区二区小视频 | 亚洲精品一区二区在线观看 | 亚洲一区二区精品视频 | 欧美三级成人理伦 | 一级毛片黄片 | 51ⅴ精品国产91久久久久久 | 久久大陆| 美女视频. | 日韩欧美视频在线 | 一区二区三区在线电影 | 国户精品久久久久久久久久久不卡 | www久久99| 国产精品大片 | 在线观看国产视频 | 亚洲成年人免费网站 | 午夜精品在线观看 | 成人免费视频播放 | 色毛片 | 国产激情一区二区三区 | 狠狠艹| 欧美精品三区 | 天天躁天天操 | 一区二区三区在线播放 | 久久久69 | 日韩国产一区二区三区 |