內(nèi)存
Linux內(nèi)存是怎么工作的
內(nèi)存映射
大多數(shù)計算機用的主存都是動態(tài)隨機訪問內(nèi)存(DRAM),只有內(nèi)核才可以直接訪問物理內(nèi)存。Linux內(nèi)核給每個進程提供了一個獨立的虛擬地址空間,并且這個地址空間是連續(xù)的。這樣進程就可以很方便的訪問內(nèi)存(虛擬內(nèi)存)。
虛擬地址空間的內(nèi)部分為內(nèi)核空間和用戶空間兩部分,不同字長的處理器地址空間的范圍不同。32位系統(tǒng)內(nèi)核空間占用1G,用戶空間占3G。64位系統(tǒng)內(nèi)核空間和用戶空間都是128T,分別占內(nèi)存空間的最高和最低處,中間部分為未定義。
并不是所有的虛擬內(nèi)存都會分配物理內(nèi)存,只有實際使用的才會。分配后的物理內(nèi)存通過內(nèi)存映射管理。為了完成內(nèi)存映射,內(nèi)核為每個進程都維護了一個頁表,記錄虛擬地址和物理地址的映射關系。頁表實際存儲在CPU的內(nèi)存管理單元MMU中,處理器可以直接通過硬件找出要訪問的內(nèi)存。
當進程訪問的虛擬地址在頁表中查不到時,系統(tǒng)會產(chǎn)生一個缺頁異常,進入內(nèi)核空間分配物理內(nèi)存,更新進程頁表,再返回用戶空間恢復進程的運行。
MMU以頁為單位管理內(nèi)存,頁大小4KB。為了解決頁表項過多問題Linux提供了多級頁表和HugePage的機制。
虛擬內(nèi)存空間分布
用戶空間內(nèi)存從低到高是五種不同的內(nèi)存段:
- 只讀段 代碼和常量等
- 數(shù)據(jù)段 全局變量等
- 堆 動態(tài)分配的內(nèi)存,從低地址開始向上增長
- 文件映射 動態(tài)庫、共享內(nèi)存等,從高地址開始向下增長
- 棧 包括局部變量和函數(shù)調(diào)用的上下文等,棧的大小是固定的。一般8MB
內(nèi)存分配與回收
分配
malloc 對應到系統(tǒng)調(diào)用上有兩種實現(xiàn)方式:
-
brk() 針對小塊內(nèi)存(<128K),通過移動堆頂位置來分配。
內(nèi)存釋放后不立即歸還內(nèi)存,而是被緩存起來。
-
mmap() 針對大塊內(nèi)存(>128K),直接用內(nèi)存映射來分配,即在文件映射段找一塊空閑內(nèi)存分配。
前者的緩存可以減少缺頁異常的發(fā)生,提高內(nèi)存訪問效率。但是由于內(nèi)存沒有歸還系統(tǒng),在內(nèi)存工作繁忙時,頻繁的內(nèi)存分配/釋放會造成內(nèi)存碎片。
后者在釋放時直接歸還系統(tǒng),所以每次mmap都會發(fā)生缺頁異常。
在內(nèi)存工作繁忙時,頻繁內(nèi)存分配會導致大量缺頁異常,使內(nèi)核管理負擔增加。
上述兩種調(diào)用并沒有真正分配內(nèi)存,這些內(nèi)存只有在首次訪問時,才通過缺頁異常進入內(nèi)核中,由內(nèi)核來分配
回收
內(nèi)存緊張時,系統(tǒng)通過以下方式來回收內(nèi)存:
- 回收緩存:LRU算法回收最近最少使用的內(nèi)存頁面;
- 回收不常訪問內(nèi)存:把不常用的內(nèi)存通過交換分區(qū)寫入磁盤
- 殺死進程:OOM內(nèi)核保護機制(進程消耗內(nèi)存越大 oom_score 越大,占用 CPU 越多 oom_score 越小,可以通過 /proc 手動調(diào)整 oom_adj)
echo -16 > /proc/$(pidof XXX)/oom_adj
如何查看內(nèi)存使用情況
free來查看整個系統(tǒng)的內(nèi)存使用情況
top/ps來查看某個進程的內(nèi)存使用情況
- VIRT 進程的虛擬內(nèi)存大小
- RES 常駐內(nèi)存的大小,即進程實際使用的物理內(nèi)存大小,不包括swap和共享內(nèi)存
- SHR 共享內(nèi)存大小,與其他進程共享的內(nèi)存,加載的動態(tài)鏈接庫以及程序代碼段
- %MEM 進程使用物理內(nèi)存占系統(tǒng)總內(nèi)存的百分比
怎樣理解內(nèi)存中的Buffer和Cache?
buffer是對磁盤數(shù)據(jù)的緩存,cache是對文件數(shù)據(jù)的緩存,它們既會用在讀請求也會用在寫請求中
如何利用系統(tǒng)緩存優(yōu)化程序的運行效率
緩存命中率
緩存命中率是指直接通過緩存獲取數(shù)據(jù)的請求次數(shù),占所有請求次數(shù)的百分比。命中率越高說明緩存帶來的收益越高,應用程序的性能也就越好。
安裝bcc包后可以通過cachestat和cachetop來監(jiān)測緩存的讀寫命中情況。
安裝pcstat后可以查看文件在內(nèi)存中的緩存大小以及緩存比例
#首先安裝Go
export GOPATH=~/go
export PATH=~/go/bin:$PATH
go get golang.org/x/sys/unix
go ge github.com/tobert/pcstat/pcstat
dd緩存加速
dd if=/dev/sda1 of=file bs=1M count=512 #生產(chǎn)一個512MB的臨時文件
echo 3 > /proc/sys/vm/drop_caches #清理緩存
pcstat file #確定剛才生成文件不在系統(tǒng)緩存中,此時cached和percent都是0
cachetop 5
dd if=file of=/dev/null bs=1M #測試文件讀取速度
#此時文件讀取性能為30+MB/s,查看cachetop結(jié)果發(fā)現(xiàn)并不是所有的讀都落在磁盤上,讀緩存命中率只有50%。
dd if=file of=/dev/null bs=1M #重復上述讀文件測試
#此時文件讀取性能為4+GB/s,讀緩存命中率為100%
pcstat file #查看文件file的緩存情況,100%全部緩存
O_DIRECT選項繞過系統(tǒng)緩存
cachetop 5
sudo docker run --privileged --name=app -itd feisky/app:io-direct
sudo docker logs app #確認案例啟動成功
#實驗結(jié)果表明每讀32MB數(shù)據(jù)都要花0.9s,且cachetop輸出中顯示1024次緩存全部命中
但是憑感覺可知如果緩存命中讀速度不應如此慢,讀次數(shù)時1024,頁大小為4K,五秒的時間內(nèi)讀取了1024*4KB數(shù)據(jù),即每秒0.8MB,和結(jié)果中32MB相差較大。說明該案例沒有充分利用緩存,懷疑系統(tǒng)調(diào)用設置了直接I/O標志繞過系統(tǒng)緩存。因此接下來觀察系統(tǒng)調(diào)用。
strace -p $(pgrep app)
#strace 結(jié)果可以看到openat打開磁盤分區(qū)/dev/sdb1,傳入參數(shù)為O_RDONLY|O_DIRECT
這就解釋了為什么讀32MB數(shù)據(jù)那么慢,直接從磁盤讀寫肯定遠遠慢于緩存。找出問題后我們再看案例的源代碼發(fā)現(xiàn)flags中指定了直接IO標志。刪除該選項后重跑,驗證性能變化。
內(nèi)存泄漏,如何定位和處理?
對應用程序來說,動態(tài)內(nèi)存的分配和回收是核心又復雜的一個邏輯功能模塊。管理內(nèi)存的過程中會發(fā)生各種各樣的“事故”:
- 沒正確回收分配的內(nèi)存,導致了泄漏
- 訪問的是已分配內(nèi)存邊界外的地址,導致程序異常退出
內(nèi)存的分配與回收
虛擬內(nèi)存分布從低到高分別是只讀段,數(shù)據(jù)段,堆,內(nèi)存映射段,棧五部分。其中會導致內(nèi)存泄漏的是:
- 堆:由應用程序自己來分配和管理,除非程序退出這些堆內(nèi)存不會被系統(tǒng)自動釋放。
- 內(nèi)存映射段:包括動態(tài)鏈接庫和共享內(nèi)存,其中共享內(nèi)存由程序自動分配和管理
內(nèi)存泄漏的危害比較大,這些忘記釋放的內(nèi)存,不僅應用程序自己不能訪問,系統(tǒng)也不能把它們再次分配給其他應用。內(nèi)存泄漏不斷累積甚至會耗盡系統(tǒng)內(nèi)存。
如何檢測內(nèi)存泄漏
預先安裝systat,docker,bcc
sudo docker run --name=app -itd feisky/app:mem-leak
sudo docker logs app
vmstat 3
可以看到free在不斷下降,buffer和cache基本保持不變。說明系統(tǒng)的內(nèi)存一致在升高。但并不能說明存在內(nèi)存泄漏。此時可以通過memleak工具來跟蹤系統(tǒng)或進程的內(nèi)存分配/釋放請求
/usr/share/bcc/tools/memleak -a -p $(pidof app)
從 memleak 輸出可以看到,應用在不停地分配內(nèi)存,并且這些分配的地址并沒有被回收。通過調(diào)用棧看到是 fibonacci 函數(shù)分配的內(nèi)存沒有釋放。定位到源碼后查看源碼來修復增加內(nèi)存釋放函數(shù)即可。
為什么系統(tǒng)的 Swap 變高
系統(tǒng)內(nèi)存資源緊張時通過內(nèi)存回收和OOM殺死進程來解決。其中可回收內(nèi)存包括:
- 緩存/緩沖區(qū),屬于可回收資源,在文件管理中通常叫做文件頁
- 在應用程序中通過fsync將臟頁同步到磁盤
- 交給系統(tǒng),內(nèi)核線程pdflush負責這些臟頁的刷新
- 被應用程序修改過暫時沒寫入磁盤的數(shù)據(jù)(臟頁),要先寫入磁盤然后才能內(nèi)存釋放
- 內(nèi)存映射獲取的文件映射頁,也可以被釋放掉,下次訪問時從文件重新讀取
對于程序自動分配的堆內(nèi)存,也就是我們在內(nèi)存管理中的匿名頁,雖然這些內(nèi)存不能直接釋放,但是 Linux 提供了 Swap 機制將不常訪問的內(nèi)存寫入到磁盤來釋放內(nèi)存,再次訪問時從磁盤讀取到內(nèi)存即可。
Swap原理
Swap本質(zhì)就是把一塊磁盤空間或者一個本地文件當作內(nèi)存來使用,包括換入和換出兩個過程:
- 換出:將進程暫時不用的內(nèi)存數(shù)據(jù)存儲到磁盤中,并釋放這些內(nèi)存
- 換入:進程再次訪問內(nèi)存時,將它們從磁盤讀到內(nèi)存中
Linux如何衡量內(nèi)存資源是否緊張?
-
直接內(nèi)存回收新的大塊內(nèi)存分配請求,但剩余內(nèi)存不足。
此時系統(tǒng)會回收一部分內(nèi)存;
-
kswapd0 內(nèi)核線程定期回收內(nèi)存。
為了衡量內(nèi)存使用情況,定義了pages_min,pages_low,pages_high 三個閾值,并根據(jù)其來進行內(nèi)存的回收操作。
-
剩余內(nèi)存 < pages_min,進程可用內(nèi)存耗盡了,只有內(nèi)核才可以分配內(nèi)存
-
pages_min < 剩余內(nèi)存 < pages_low,內(nèi)存壓力較大,kswapd0執(zhí)行內(nèi)存回收,直到剩余內(nèi)存 > pages_high
-
pages_low < 剩余內(nèi)存 < pages_high,內(nèi)存有一定壓力,但可以滿足新內(nèi)存請求
-
剩余內(nèi)存 > pages_high,說明剩余內(nèi)存較多,無內(nèi)存壓力
pages_low = pages_min 5 / 4 pages_high = pages_min 3 / 2
-
NUMA 與 SWAP
很多情況下系統(tǒng)剩余內(nèi)存較多,但 SWAP 依舊升高,這是由于處理器的 NUMA 架構(gòu)。
在NUMA架構(gòu)下多個處理器劃分到不同的 Node,每個Node都擁有自己的本地內(nèi)存空間。在分析內(nèi)存的使用時應該針對每個Node單獨分析
numactl --hardware #查看處理器在Node的分布情況,以及每個Node的內(nèi)存使用情況
內(nèi)存三個閾值可以通過 /proc/zoneinfo 來查看,該文件中還包括活躍和非活躍的匿名頁/文件頁數(shù)。
當某個Node內(nèi)存不足時,系統(tǒng)可以從其他Node尋找空閑資源,也可以從本地內(nèi)存中回收內(nèi)存。通過/proc/sys/vm/zone_raclaim_mode來調(diào)整。
- 0表示既可以從其他Node尋找空閑資源,也可以從本地回收內(nèi)存
- 1,2,4 表示只回收本地內(nèi)存,2表示可以會回臟數(shù)據(jù)回收內(nèi)存,4表示可以用Swap方式回收內(nèi)存。
swappiness
在實際回收過程中Linux根據(jù) /proc/sys/vm/swapiness 選項來調(diào)整使用Swap的積極程度,從 0-100,數(shù)值越大越積極使用 Swap,即更傾向于回收匿名頁;數(shù)值越小越消極使用 Swap,即更傾向于回收文件頁。
注意:這只是調(diào)整 Swap 積極程度的權(quán)重,即使設置為0,當剩余內(nèi)存+文件頁小于頁高閾值時,還是會發(fā)生 Swap。
Swap升高時如何定位分析
free #首先通過free查看swap使用情況,若swap=0表示未配置Swap
#先創(chuàng)建并開啟swap
fallocate -l 8G /mnt/swapfile
chmod 600 /mnt/swapfile
mkswap /mnt/swapfile
swapon /mnt/swapfile
free #再次執(zhí)行free確保Swap配置成功
dd if=/dev/sda1 of=/dev/null bs=1G count=2048 #模擬大文件讀取
sar -r -S 1 #查看內(nèi)存各個指標變化 -r內(nèi)存 -S swap
#根據(jù)結(jié)果可以看出,%memused在不斷增長,剩余內(nèi)存kbmemfress不斷減少,緩沖區(qū)kbbuffers不斷增大,由此可知剩余內(nèi)存不斷分配給了緩沖區(qū)
#一段時間之后,剩余內(nèi)存很小,而緩沖區(qū)占用了大部分內(nèi)存。此時Swap使用之間增大,緩沖區(qū)和剩余內(nèi)存只在小范圍波動
停下sar命令
cachetop5 #觀察緩存
#可以看到dd進程讀寫只有50%的命中率,未命中數(shù)為4w+頁,說明正式dd進程導致緩沖區(qū)使用升高
watch -d grep -A 15 ‘Normal’ /proc/zoneinfo #觀察內(nèi)存指標變化
#發(fā)現(xiàn)升級內(nèi)存在一個小范圍不停的波動,低于頁低閾值時會突然增大到一個大于頁高閾值的值
說明剩余內(nèi)存和緩沖區(qū)的波動變化正是由于內(nèi)存回收和緩存再次分配的循環(huán)往復。有時候 Swap 用的多,有時候緩沖區(qū)波動更多。此時查看 swappiness 值為60,是一個相對中和的配置,系統(tǒng)會根據(jù)實際運行情況來選去合適的回收類型。
如何“快準狠”找到系統(tǒng)內(nèi)存存在的問題
內(nèi)存性能指標
- 系統(tǒng)內(nèi)存指標
- 已用內(nèi)存/剩余內(nèi)存
- 共享內(nèi)存 (tmpfs實現(xiàn))
- 可用內(nèi)存:包括剩余內(nèi)存和可回收內(nèi)存
- 緩存:磁盤讀取文件的頁緩存,slab分配器中的可回收部分
- 緩沖區(qū):原始磁盤塊的臨時存儲,緩存將要寫入磁盤的數(shù)據(jù)
進程內(nèi)存指標
- 虛擬內(nèi)存:5大部分
- 常駐內(nèi)存:進程實際使用的物理內(nèi)存,不包括Swap和共享內(nèi)存
- 共享內(nèi)存:與其他進程共享的內(nèi)存,以及動態(tài)鏈接庫和程序的代碼段
- Swap 內(nèi)存:通過Swap換出到磁盤的內(nèi)存
缺頁異常
- 可以直接從物理內(nèi)存中分配,次缺頁異常
- 需要磁盤 IO 介入(如 Swap),主缺頁異常。此時內(nèi)存訪問會慢很多
內(nèi)存性能工具
根據(jù)不同的性能指標來找合適的工具:
內(nèi)存分析工具包含的性能指標:
-
DRAM
+關注
關注
40文章
2326瀏覽量
183868 -
Linux
+關注
關注
87文章
11345瀏覽量
210400 -
計算機
+關注
關注
19文章
7537瀏覽量
88643 -
虛擬內(nèi)存
+關注
關注
0文章
77瀏覽量
8087
發(fā)布評論請先 登錄
相關推薦
HBase性能優(yōu)化方法總結(jié)
Linux系統(tǒng)的性能優(yōu)化策略
基于Linux的Socket網(wǎng)絡編程的性能優(yōu)化
![基于<b class='flag-5'>Linux</b>的Socket網(wǎng)絡編程的<b class='flag-5'>性能</b><b class='flag-5'>優(yōu)化</b>](https://file1.elecfans.com//web2/M00/A5/54/wKgZomUMN-yAaps9AAEmhQn-iPM109.jpg)
Linux CPU的性能應該如何優(yōu)化
Linux的基礎學習筆記資料總結(jié)
Linux下Apache性能分析總結(jié)
![<b class='flag-5'>Linux</b>下Apache<b class='flag-5'>性能</b>分析<b class='flag-5'>總結(jié)</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
Linux 性能優(yōu)化總結(jié)!1
![<b class='flag-5'>Linux</b> <b class='flag-5'>性能</b><b class='flag-5'>優(yōu)化</b><b class='flag-5'>總結(jié)</b>!1](https://file1.elecfans.com/web2/M00/82/B6/wKgZomRd5ymAbDHUAACWE6UbNxM949.jpg)
Linux 性能優(yōu)化總結(jié)!3
Linux內(nèi)核slab性能優(yōu)化的核心思想
![<b class='flag-5'>Linux</b>內(nèi)核slab<b class='flag-5'>性能</b><b class='flag-5'>優(yōu)化</b>的核心思想](https://file1.elecfans.com/web2/M00/AF/B9/wKgZomVRm7CAZLMyAAIDou_e_HI165.jpg)
評論