Linux系統性能優化二:詳解Linux內存原理

Linux內存是怎麼工作的

通過虛擬地址訪問物理地址

首先我們8g的內存,其實指的是物理內存,也就是主存,

在Linux系統裏邊,我們的進程是不能直接訪問物理內存的,它會爲每個進程分配一個虛擬地址內存,然後通過虛擬地址內存去訪問,

在CPU的內存管理單元裏邊有一個頁表,它會把我們的虛擬內存跟物理內存之間做一個映射,

只有那些實際使用的虛擬內存纔會分配物理內存,也就是提供一個內存映射的關係

當進程訪問的虛擬地址在列表中查不到的時候,就會產生一個缺頁異常,然後就會重新分配物理內存,然後更新頁表,再返回給用戶空間,這樣進程就恢復正常

內存映射的最小單位是頁,這個頁一般是比較小的,一般是4kb左右,

它頁比較小,就有一個問題,因爲這個頁比較小,所以需要比較多的頁才能提供這個內存映射的關係,所以整個頁表會變得非常大,

那麼裏面爲了解決這個頁表變得非常大的問題,Linux有兩種機制,一個是多級頁表,一個是大頁,

Linux的多級頁表採用的是4級頁表,它通過將原來的映射關係,改成區塊索引以及區塊內的偏移,

因爲虛擬內存空間通常只用到了很少的一部分,多級頁表就只保存這些用到的虛擬內存,這樣就可以減少那個頁表的數量,

還有一種機制是大頁,也就是分配比一般的頁更大的頁,常見的有2MB,1GB,大頁通常用在那些使用大量內存的進程上,比如 Oracle,還有es,logstash他們佔用的內存也挺多的,

內存的分配與回收

malloc() 是 C 標準庫提供的內存分配函數,對應到系統調用上,有兩種實現方式,即 brk() 和 mmap()。

對小塊內存(小於 128K),C 標準庫使用 brk() 來分配,也就是通過移動堆頂的位置來分配內存。這些內存釋放後並不會立刻歸還系統,而是被緩存起來,這樣就可以重複使用。

而大塊內存(大於 128K),則直接使用內存映射 mmap() 來分配,也就是在文件映射段找一塊空閒內存分配出去。

brk() 方式的緩存,釋放後不會立即歸還給系統,可以減少缺頁異常的發生,提高內存訪問效率。當時,因爲這些內存沒有歸還系統,所以在內存工作繁忙時,頻繁的內存分配和釋放會造成內存碎片。

而 mmap() 方式分配的內存,會在釋放時直接歸還系統,所以每次 mmap 都會發生缺頁異常。在內存工作繁忙時,頻繁的內存分配會導致大量的缺頁異常,使內核的管理負擔增大。這也是malloc 只對大塊內存使用 mmap 的原因

對內存來說,如果只分配而不釋放,就會造成內存泄漏,甚至會耗盡系統內存。所以,在應用程序用完內存後,還需要調用 free() 或 unmap() ,來釋放這些不用的內存

在發現內存緊張時,系統也有一系列機制來回收內存,

  • 回收緩存,使用LRU(least Recently Used)算法,對那些最近最少使用的的內存頁面進行回收
  • 將那些不常訪問的內存放到swap交換分區
  • OOM機制(out of memory),直接殺掉佔用大量內存的進程。
Linux的swap機制

Swap 把這些不常訪問的內存先寫到磁盤中,然後釋放這些內存,給其他更需要的進程使用。再次訪問這些不常訪問的內存的時時候,重新從磁盤讀入內存就可以了。

簡單來說,Swap 說白了就是把一塊磁盤空間或者一個本地文件(以下講解以磁盤爲例),當成內存來使用。它包括換出和換入兩個過程

  • 換出就是把進程暫時沒有用到的不常用的內存放到磁盤裏面
  • 換入就是當進程想再次訪問這些內存的時候,再把它從磁盤換到內存

swap它把我們系統的可用內存變大了,但是因爲硬盤比內存慢得多,所以如果swap被大量使用了,就要進一步排查。

平時我們筆記本電腦休眠的時候,利用的就是swap的原理,休眠時,把系統的內存存入磁盤,這樣等到再次開機時,只要從磁盤中加載內存就可以。這樣就省去了很多應用程序的初始化過程,加快了開機速度。

swap是用來回收內存的,對於回收內存,最容易想到的場景就是有一個新的大塊內存需要分配,但是現在內存不夠,這時候就需要回收一部分內存,它被稱爲直接內存回收

另外,還有一個專門的內核線程用來定期回收內存,叫kswapd0。kswapd0 定義了三個內存閾值(watermark,也稱爲水位),分別是

頁最小閾值(pages_min)、頁低閾值(pages_low)和頁高閾值(pages_high)。剩餘內存,則使用 pages_free 表示。
在這裏插入圖片描述

  • 剩餘內存小於頁最小閾值,說明進程可用內存都耗盡了,只有內核纔可以分配內存
  • 剩餘內存落在頁最小閾值和頁低閾值中間,說明內存壓力比較大,這時 kswapd0 會執行內存回收,直到剩餘內存大於高閾值爲止。

另外,也有可能出現可用內存還有很多,但是swap被使用得也很多。可能是因爲一些進程的內存佔用比較大,所以佔用了swap內存,但是當這個進程結束的時候,它的可用內存就恢復得比較大了。但是由於之前他佔用了交換分區,交換分區裏面的內存還沒有放到可用內存裏邊,還是放在磁盤裏邊的,所以就會造成這種情況發生。

查看內存

free

[root@VM_0_11_centos ~]# free
              total        used        free      shared  buff/cache   available
Mem:        1882056      385240       78224         624     1418592     1308716
Swap:             0           0           0

最後一列的可用內存 available 。available 不僅包含未使用內存,還包括了可回收的緩存。但是,並不是所有緩存都可以回收,因爲有些緩存可能正在使用中。

free 顯示的是整個系統的內存使用情況。如果你想查看進程的內存使用情況,使用 top

[root@VM_0_11_centos ~]# top
top - 20:22:42 up 25 days,  5:36,  1 user,  load average: 0.00, 0.01, 0.05
Tasks:  90 total,   2 running,  88 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1882056 total,    74196 free,   385740 used,  1422120 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1308224 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                
    1 root      20   0  191040   2852   1408 S  0.0  0.2   4:43.55 systemd                                                                                                
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.02 kthreadd                                                                                               
    4 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kworker/0:0H                                                                                           
    6 root      20   0       0      0      0 S  0.0  0.0   0:32.51 ksoftirqd/0    
  • VIRT 是進程虛擬內存的大小,只要是進程申請過的內存,即便還沒有真正分配物理內存,也會計算在內。
  • RES 是常駐內存的大小,也就是進程實際使用的物理內存大小,但不包括 Swap 和共享內存。
  • SHR 是共享內存的大小,比如與其他進程共同使用的共享內存、加載的動態鏈接庫以及程序的代碼段等。
  • %MEM 是進程使用物理內存佔系統總內存的百分比。

如何理解cache和buffer

在free命令裏面其他指標都很好理解,然後有個buffer和cache,它的字面意識是buffer是緩衝,cache是緩存,之前我man free去查看過他的定義,

buffers
              Memory used by kernel buffers (Buffers in /proc/meminfo)
 
       cache  Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)
 
       buff/cache
              Sum of buffers and cache

他的幫助手冊裏面顯示:

  • buffer是用於內核緩衝區的內存大小
  • cache是用於頁緩存和slabs的內存大小

它的幫助手冊裏面告訴我們,這些數值都來自 /proc/meminfo。實際上,很多的性能分析工具,他們的數據都是從/proc/這個目錄裏面獲取的。所以我們可以繼續man一下proc目錄,

它這裏的定義是

  • buffer是對原始磁盤數據塊的臨時存儲,也就是緩存磁盤的數據,通常只有20兆左右,它是用來優化數據寫入磁盤的,可以把多次分散的寫集中起來,統一成一次來寫入,。
  • Cached 是從磁盤讀取文件的頁緩存,也就是用來緩存從文件讀取的數據。這樣,下次訪問這些文件數據時,就可以直接從內存中快速獲取,而不需要再次訪問緩慢的磁盤。

Buffer 的文檔沒有提到這是磁盤讀數據還是寫數據的緩存,而在很多網上資料裏面,都會提到 Buffer 只是對將要寫入磁盤數據的緩存

文檔中提到,Cache 是對從文件讀取數據的緩存,那麼它是不是也會緩存寫文件的數據呢?

我之前使用dd命令分別進行磁盤和文件的寫和讀的實驗,然後開另一個終端用vmstat來觀察,發現:

  • Buffer 既可以用作“將要寫入磁盤數據的緩存”,也可以用作“從磁盤讀取數據的緩存”。
  • Cache 既可以用作“從文件讀取數據的頁緩存”,也可以用作“寫文件的頁緩存”。

簡單來說,Buffer 是對磁盤數據的緩存,而 Cache 是文件數據的緩存,它們既會用在讀請求中,也會用在寫請求中。

如何快準狠定位內存問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章