在Linux中經常發現空閑記憶體很少,似乎所有的記憶體都被系統佔用了,表面感覺是記憶體不夠用了,其實不然。這是Linux記憶體管理的一個優秀特性,主要特點是,無論實體記憶體有多大,Linux 都將其充份利用,將一些程式調用過的硬碟資料讀入記憶體(buffer/cache),利用記憶體讀寫的高速特性來提高Linux系統的資料訪問效能。在這方面,區別於Windows的記憶體管理。本文從Linux的記憶體管理機制入手,簡單介紹linux如何使用記憶體、監控記憶體,linux與windows記憶體管理上的區別簡介,linux記憶體使用量的一大特點(buffer/cache的異同)。
一、Linux記憶體管理機制
1、實體記憶體和虛擬記憶體 我們知道,直接從實體記憶體讀寫資料要比從硬碟讀寫資料要快的多,因此,我們希望所有資料的讀取和寫入都在記憶體完成,而記憶體是有限的,這樣就引出了實體記憶體與虛擬記憶體的概念。
實體記憶體就是系統硬體提供的記憶體大小,是真正的記憶體,相對於實體記憶體,在linux下還有一個虛擬記憶體的概念,虛擬記憶體就是為了滿足實體記憶體的不足而提出的策略,它是利用磁碟空間虛擬出的一塊邏輯記憶體,用作虛擬記憶體的磁碟空間被稱為交換空間(Swap Space)。
作為實體記憶體的擴充,linux會 在實體記憶體不足(注意這一條件,這一條件的量化分析請參考https://www.douban.com/note/349467816/) 時,使用交換分區的虛擬記憶體,更詳細的說,就是核心會將暫時不用的記憶體塊資訊寫到交換空間,這樣以來,實體記憶體得到了釋放,這塊記憶體就可以用於其它目的,當需要用到原始的內容時,這些資訊會被重新從交換空間讀入實體記憶體。
Linux的記憶體管理採取的是分頁存取機制(詳細可參考http://www.linuxeye.com/Linux/1931.html),為了保證實體記憶體能得到充分的利用,核心會在適當的時候將實體記憶體中不經常使用的資料區塊自動交換到虛擬記憶體中,而將經常使用的資訊保留到實體記憶體。
要深入瞭解linux記憶體運行機制,需要知道下面提到的幾個方面:
Linux系統會根據系統配置不時地進行頁面交換操作,以保持一定量的空閑實體記憶體,有些配置下即使並沒有什麼事情需要記憶體,Linux也會交換出暫時不用的記憶體頁面。這可以避免等待交換所需的時間。相關的配置有/etc/sysctl.conf中的vm.swappiness配置(配製方法請參考http://www.vcaptain.com/?id=17),該參數的作用簡單描述就是“當 swappiness 內容的值為 0 時,表示最大限度地使用實體記憶體,實體記憶體使用完畢後,才會使用 swap 分區;當 swappiness 內容的值為 100 時,表示積極地使用 swap 分區,並且把記憶體中的資料及時地置換到 swap 分區。Linux 系統初始安裝完成時,其預設值為 60, 這表示空閑實體記憶體少於 60% 時開始啟用記憶體置換演算法,將記憶體中不常使用的資料置換到 swap 分區。”(具體如何起作用請參考https://www.douban.com/note/349467816/) Linux 進行頁面交換是有條件的,不是所有頁面在不用時都交換到虛擬記憶體,linux核心根據”最近最經常使用“演算法,僅僅將一些不經常使用的分頁檔交換到虛擬 記憶體,有時我們會看到這麼一個現象:linux實體記憶體還有很多,但是交換空間也使用了很多。其實,這並不奇怪,例如:一個佔用很大記憶體的進程運行時,需要耗費很多記憶體資源,此時就會有一些不常用分頁檔被交換到虛擬記憶體中,但後來這個佔用很多記憶體資源的進程結束並釋放了很多記憶體時,剛才被交換出去的頁面 檔案並不會自動的交換進實體記憶體,除非有這個必要,那麼此刻系統實體記憶體就會空閑很多,同時交換空間也在被使用,就出現了剛才所說的現象了。關於這點,不用擔心什麼,只要知道是怎麼一回事就可以了。 交換空間的頁面在使用時會首先被交換到實體記憶體,如果此時沒有足夠的實體記憶體來容納這些頁 面,它們又會被馬上交換出去,如此以來,虛擬記憶體中可能沒有足夠空間來儲存這些交換頁面,最終會導致linux出現假死機、服務異常等問題,linux雖然可以在一段時間內自行恢複,但是恢複後的系統已經基本不可用了。 分配太多的Swap空間會浪費磁碟空間,而Swap空間太少,則系統會發生錯誤。 如果系統的實體記憶體用光了,系統就會跑得很慢,但仍能運行;如果Swap空間用光了,那麼系統就會發生錯誤。例如,Web伺服器能根據不同的請求數量衍生出多個服務進程(或線程),如果Swap空間用完,則服務進程無法啟動,通常會出現“application is out of memory”的錯誤,嚴重時會造成服務進程的死結。因此Swap空間的分配是很重要的。
因此,合理規劃和設計Linux記憶體的使用,是非常重要的。
二、記憶體的監控 作為一名Linux系統管理員,監控記憶體的使用狀態是非常重要的,通過監控有助於瞭解記憶體的使用狀態,比如記憶體佔用是否正常,記憶體是否緊缺等等,監控記憶體最常使用的命令有free、 top 等,下面是某個系統free的輸出(free預設單位是KB):
[root@linuxeye ~]# free total used free shared buffers cachedMem: 3894036 3473544 420492 0 72972 1332348-/+ buffers/cache: 2068224 1825812Swap: 4095992 906036 3189956
我們使用total1、used1、 free1、used2、free2 等名稱來代表上面統計資料的各值,1、2 分別代表第一行(不考慮標題列)和第二行 (不考慮標題列)的資料。 --------第一行資料:代表核心角度的統計--------
total1:表示實體記憶體總量。
used1:表示已使用(已指派出去)的實體記憶體總量,包括真正已使用和分配給緩衝(包含buffers 與cached)的數量。
free1:未被分配的實體記憶體。
shared1:共用記憶體,一般系統不會用到,這裡也不討論。
buffers1: 系統分配給buffers 的記憶體大小。
cached1:系統分配給cached 的記憶體數量。buffer 與cache 的區別見後面。 --------第二行資料:代表應用角度的統計--------
used2:實際使用記憶體總量。
free2:系統當前實際可用記憶體,包括未被分配的記憶體以及分配給buffers 與cached 的記憶體之和。
可以整理出如下等式:
total1 = used1 + free1
total1 = used2 + free2
used1 = buffers1 + cached1 + used2
free2 = buffers1 + cached1 + free1
上面提到free命令輸出的記憶體狀態,可以通過兩個角度來查看:一個是從核心的角度來看,一個是從應用程式層的角度來看的:
1、從核心的角度來查看記憶體的狀態 就是核心目前可以直接分配到,不需要額外的操作,即為上面free命令輸出中第二行Mem項的值,可以看出,此系統實體記憶體有3894036K,閒置記憶體只有420492KB,也就是410MB多一點,我們來做一個這樣的計算: total1 - used1 = free1
3894036 - 3473544 = 420492
其實就是總的實體記憶體減去已指派的實體記憶體得到的就是閒置實體記憶體大小,注意這裡的可用記憶體值420492K並不包含處於buffers和cached狀態的記憶體大小。 如果你認為這個系統空閑記憶體太小,那你就錯了,實際上,核心完全控制著記憶體的使用方式,Linux會在需要記憶體的時候,或在系統運行逐步推進時,將buffers和cached狀態的記憶體變為free狀態的記憶體,以供系統使用。
2、從應用程式層的角度來看系統記憶體的使用狀態 也就是Linux上啟動並執行應用程式可以使用的記憶體大小,即free命令第三行(這裡說的第幾行包含了標題列) -/+ buffers/cached 的輸出,可以看到,此系統已經使用的記憶體才2068224K,而閒置記憶體達到1825812K,繼續做這樣一個計算: free2 = buffers1 + cached1 + free1
420492+(72972+1332348)=1825812
通過這個等式可知,應用程式可用的實體記憶體值是Mem項的free值(free1)加上buffers1和cached1值之和,也就是說,這個free值是包括buffers和cached項大小的, 對於應用程式來說,buffers/cached佔有的記憶體是可用的,因為buffers/cached是為了提高檔案讀取的效能,當應用程式需要用到記憶體的時候,buffers/cached會很快地被回收,以供應用程式使用 。
三、linux和windows記憶體管理的區別 Linux 優先使用實體記憶體,當實體記憶體還有空閑時,linux是不會釋放記憶體的,即時佔用記憶體的程式已經被關閉了(這部分記憶體就用來做緩衝了)。也就是說,即時你有很大的記憶體,用過一段時間後,也會被佔滿。這樣做的好處是,啟動那些剛開啟過的程式、或是讀取剛存取過得資料會比較快,對於伺服器很有好處。
windows則總是給記憶體留下一定的空閑空間,即時記憶體有空閑也會讓程式使用一些虛擬記憶體,這樣做的好處是,啟動新的程式比較快,直接分給它些空閑記憶體就可以了,而linux下呢。由於記憶體經常處於全部被使用的狀態,則要先清理出一塊記憶體,再分配給新的程式使用,因此,新程式的啟動會慢一些。
四、buffers與cached 1、異同點 在Linux 作業系統中,當應用程式需要讀取檔案中的資料時,作業系統先分配一些記憶體,將資料從磁碟讀入到這些記憶體中,然後再將資料分發給應用程式;當需要往檔案中寫 資料時,作業系統先分配記憶體接收使用者資料,然後再將資料從記憶體寫到磁碟上。然而,如果有大量資料需要從磁碟讀取到記憶體或者由記憶體寫入磁碟時,系統的讀寫性 能就變得非常低下,因為無論是從磁碟讀資料,還是寫資料到磁碟,都是一個很消耗時間和資源的過程,在這種情況下,Linux引入了buffers和 cached機制。
buffers與cached都是記憶體操作,用來儲存系統曾經開啟過的檔案以及檔案屬性資訊,這樣當作業系統需要讀取某些檔案時,會首先在buffers 與cached記憶體區尋找,如果找到,直接讀出傳送給應用程式,如果沒有找到需要資料,才從磁碟讀取,這就是作業系統的緩衝機制,通過緩衝,大大提高了操 作系統的效能。但buffers與cached緩衝的內容卻是不同的。
buffers是用來緩衝塊裝置做的,它只記錄檔案系統的中繼資料(metadata)以及 tracking in-flight pages ,而 cached是用來給檔案做緩衝 。更通俗一點說:buffers主要用來存放目錄裡面有什麼內容,檔案的屬性以及許可權等等。而cached直接用來記憶我們開啟過的檔案和程式。
為了驗證我們的結論是否正確,可以通過vi開啟一個非常大的檔案,看看cached的變化,然後再次vi這個檔案,感覺一下兩次開啟的速度有何異同,是不是第二次開啟的速度明顯快於第一次呢。這裡提供一個小指令碼列印首次及第二次開啟一個大檔案(catalina.logaa 約2G)耗時及cached/buffers的變化:
#!/bin/bashsyncsyncecho 3 > /proc/sys/vm/drop_caches echo -e "----------------------緩衝釋放後,記憶體使用量情況(KB):----------------------"freecached1=`free |grep Mem:|awk '{print $7}'`buffers1=`free |grep Mem:|awk '{print $6}'`date1=`date +"%Y%m%d%H%M%S"`cat catalina.logaa >1date2=`date +"%Y%m%d%H%M%S"`echo -e "----------------------首次讀取大檔案後,記憶體使用量情況(KB):----------------------"freecached2=`free |grep Mem:|awk '{print $7}'`buffers2=`free |grep Mem:|awk '{print $6}'`#echo $date1#echo $date2interval_1=`expr ${date2} - ${date1}`cached_increment1=`expr ${cached2} - ${cached1}`buffers_increment1=`expr ${buffers2} - ${buffers1}`date3=`date +"%Y%m%d%H%M%S"`cat catalina.logaa >1date4=`date +"%Y%m%d%H%M%S"`echo -e "----------------------再次讀取大檔案後,記憶體使用量情況(KB):----------------------"freecached3=`free |grep Mem:|awk '{print $7}'`buffers3=`free |grep Mem:|awk '{print $6}'`#echo $date3#echo $date4interval_2=`expr ${date4} - ${date3}`cached_increment2=`expr ${cached3} - ${cached2}`buffers_increment2=`expr ${buffers3} - ${buffers2}`echo -e "----------------------統計摘要資料如下:----------------------"echo -e "首次讀取大檔案,cached增量:${cached_increment1},單位:KB"echo -e "首次讀取大檔案,buffers增量:${buffers_increment1},單位:KB"echo -e "首次讀取大檔案,耗時:${interval_1},單位:s \n"echo -e "再次讀取大檔案,cached增量:${cached_increment2},單位:KB"echo -e "再次讀取大檔案,buffers增量:${buffers_increment2},單位:KB"echo -e "再次讀取大檔案,耗時:${interval_2},單位:s"執行結果如下(由於列印出來的free結果跟參數賦值時用的free命令之間有時間間隔,計算起來可能會略有不同):
接著執行下面的命令:find /* -name *.conf , 看看buffers的值是否變化,然後重複執行find命令,看看兩次顯示速度有何不同。如下指令碼(需要注意使用bc計算浮點型資料時需要安裝相應軟體,我的系統是centos7.0,核心4.3.3的版本,安裝的是bc-1.06.95-13.el7.x86_64服務):
#!/bin/bashsyncsyncecho 3 > /proc/sys/vm/drop_cachesecho -e "----------------------緩衝釋放後,記憶體使用量情況(KB):----------------------"freecached1=`free |grep Mem:|awk '{print $7}'`buffers1=`free |grep Mem:|awk '{print $6}'`date1=`date +%s.%N`find /* -name *.conf >2date2=`date +%s.%N`echo -e "----------------------首次查詢後,記憶體使用量情況(KB):----------------------"freecached2=`free |grep Mem:|awk '{print $7}'`buffers2=`free |grep Mem:|awk '{print $6}'`#echo $date1#echo $date2interval_1=`echo "scale=3; ${date2} - ${date1}" | bc`cached_increment1=`expr ${cached2} - ${cached1}`buffers_increment1=`expr ${buffers2} - ${buffers1}`date3=`date +%s.%N`find /* -name *.conf >2date4=`date +%s.%N`echo -e "----------------------重新查詢後,記憶體使用量情況(KB):----------------------"freecached3=`free |grep Mem:|awk '{print $7}'`buffers3=`free |grep Mem:|awk '{print $6}'`#echo $date3#echo $date4interval_2=`echo "scale=3; ${date4} - ${date3}" | bc`cached_increment2=`expr ${cached3} - ${cached2}`buffers_increment2=`expr ${buffers3} - ${buffers2}`echo -e "----------------------統計摘要資料如下:----------------------"echo -e "首次查詢,cached增量:${cached_increment1},單位:KB"echo -e "首次查詢,buffers增量:${buffers_increment1},單位:KB"echo -e "首次查詢,耗時:${interval_1},單位:s \n"echo -e "重新查詢,cached增量:${cached_increment2},單位:KB"echo -e "重新查詢,buffers增量:${buffers_increment2},單位:KB"echo -e "重新查詢,耗時:${interval_2},單位:s"結果如下(最後那個應該是0.470702440,使用bc計算的時候那個0被去掉了):
2、記憶體釋放 linux系統中/proc是一個虛擬檔案系統,我們可以通過對它的讀寫操作做為與kernel實體間進行通訊的一種手段。也就是說可以通過修改/proc中的檔案,來對當前kernel的行為做出調整。那麼我們可以通過調整/proc/sys/vm/drop_caches來釋放記憶體。關於drop_caches,官方給出的說法是:
Writing to this will cause thekernel to drop clean caches, dentries andinodes from memory, causing thatmemory to become free.To free pagecache: echo 1 > /proc/sys/vm/drop_cachesTo free dentries and inodes: echo 2 > /proc/sys/vm/drop_cachesTo free pagecache, dentries andinodes: echo 3 > /proc/sys/vm/drop_cachesAs this is a non-destructiveoperation and dirty objects are not freeable, theuser should run `sync' first.http://www.kernel.org/doc/Documentation/sysctl/vm.txt
# cat /proc/sys/vm/drop_caches
0
預設是0,1表示清空頁緩衝,2表示清空inode和分類樹緩衝,3清空所有的緩衝
[root@hps103 ~]# sync
[root@hps103 ~]# free -m
total used free shared buffers cached
Mem: 499 323 175 0 52 188
-/+ buffers/cache: 82 416
Swap: 2047 0 2047
[root@hps103 ~]# echo 3 > /proc/sys/vm/drop_caches
[root@hps103 ~]# free -m //發現緩衝明顯減少了
total used free shared buffers cached
Mem: 499 83 415 0 1 17
-/+ buffers/cache: 64 434
Swap: 2047 0 2047
五、總結 Linux作業系統的記憶體運行原理,很大程度上是根據伺服器的需求來設計的,例如系統的緩衝機制會把經常使用到的檔案和資料緩衝在cached 中,linux總是在力求緩衝更多的資料和資訊,這樣再次需要這些資料時可以直接從記憶體中取,而不需要有一個漫長的磁碟操作,這種設計思路提高了系統的整體效能。
主要參考文章:http://www.linuxeye.com/Linux/1932.html