【Android Linux內存及性能優化】六 系統內存的優化
本文接着
《【Android Linux內存及性能優化】(一) 進程內存的優化 - 堆段》
《【Android Linux內存及性能優化】(二) 進程內存的優化 - 棧段 - 環境變量 - ELF》
《【Android Linux內存及性能優化】(三) 進程內存的優化 - ELF執行文件的 數據段-代碼段》
《【Android Linux內存及性能優化】(四) 進程內存的優化 - 動態庫- 靜態庫》
《【Android Linux內存及性能優化】(五) 進程內存的優化 - 線程》
一、內存篇
1.1 系統當前可用內存
1.2 進程的內存使用
1.3 進程內存優化
1.4 系統內存優化
1.4.1 守護進程的內存使用
在設備開機之後,你會發現已經有很多進程在運行,這些進程將一直存活,直到設備關機,我們把這類進程稱爲守護進程。
守護進程對系弘的內存使用影響巨大:
(1)由於守護進程一直存活,所以其所佔用的內存將不會被釋放。
(2)一個進程即使什麼事情都不做,它引用了一些動態庫,也會佔用大量的物理內存。
(3)由於守護進程的生存週期很長,其哪怕只有一點內存泄漏,也會導致系統的內存耗盡。
進程一旦退出,其所佔用的內存存將被 Linux內核收回,所以對那些生存週期很短的進程來講,即使有些內存泄漏,其對系統內存使用影響並不大。
因些應該儘量減少系統中守護進程的數量,對於那些提供服務的守護進程來講,儘量做到在進程需要的時候來啓動,在不需要的時候退出。
下面就來詳細描述如何減少守護進程的數量。
首先爲什麼會存在守護進程? 原因主要兩點:
-
系統需要守護進程來偵聽某些事件。
在設計守護進程時,開發人員往往沒有區分其中的常駐部分和非常駐部分,
在偵聽到一個事件後,直接啓動相應的業務邏輯,而在這些業務邏輯執行完之後,並不會將本已無用的業務邏輯部分釋放。
這樣就造成了偵聽部分與業務邏輯部分混在一起都作爲守護進程,
而使得守護進程部分邏輯比較複雜,一方面佔用了大量的內存,另一方面也增大了內存泄漏的機率。因些建議將守護進程明確分爲常駐部分和非常駐部分,分別讓不同的進程來完成,將常駐內存部分的邏輯儘量簡化。
更進一步,可以考慮將幾個守護進程的偵聽事件部分合併到一個守護進程,當負責偵聽的守護進程收到消息後,再去啓動相應的業務邏輯。
在這一方面,inetd 是最好的例子,其常駐內部部分可以偵聽 telnet、ftp等端口,在有用戶該問後,又會創建相應的進程爲用戶提供服務。
- 有些進程提供服務的響應時間達不到要求,開發人員便將其放在設備開機時啓動,所以轉變爲了守護進程。
首先需要考慮加快進程的啓動速度,從而使服務進程達到按需啓動的需求,這個後續分析。
除了優化加載動態庫、使用 Prelink 等方法外,還可以採用一些進程調度的方法來減少守護進程對於內存的影響。
1.4.2 tmpfs 分區
在LInux 中,爲了加快對文件的讀寫,基於內存建立了一個文件系統,稱爲 ramdisk 或者 tmpfs。
對於該文件系統的訪問,都將直接操作物理內存,因此而要比訪問Flash 快得多。
在做系統內存優化時,不要忘掉這塊內存。
ciellee@sh:~/work/code/simcom_220c_sop_1028_0426$ df -H
Filesystem Size Used Avail Use% Mounted on
udev 8.4G 0 8.4G 0% /dev
tmpfs 1.7G 11M 1.7G 1% /run
/dev/sda8 706G 520G 150G 78% /
tmpfs 8.4G 180M 8.2G 3% /dev/shm
tmpfs 5.3M 4.1k 5.3M 1% /run/lock
tmpfs 8.4G 0 8.4G 0% /sys/fs/cgroup
/dev/loop0 99M 99M 0 100% /snap/core/9066
可以看出,tmpfs 文件分區爲8.4G。
在對這個文件分區進行讀寫時,要時刻提醒自已,它是佔用物理內存的,在該分區上的文件,不需要的時候應及時刪除。
1.4.3 Cache 緩存 和 Buffer 緩衝
前面講到, 系統中空閒內存 = MemFree + Buffers + Cached。
下面我們來看下 Cache 和 Buffer 分別是什麼。
1.4.3.1 Cache 緩存
Cache 也稱緩存,是把從Flash 中讀取的數據保存起來,
若再次讀取該塊數據時,就不需要去讀取Flash 了,直接從緩存中讀取,從而提高讀取文件的速度。
Cache 緩存的數據會根據讀取頻率進行組織,把最頻繁讀取的內容 放在最容易找到的位置,把不再讀的內容不斷往後排,直至從中刪掉。
在程序執行過程中,發現某些指令不在內存中,便會產生 page fault,而將代碼段的數據載入到物理內存。當進程退出後,代碼段所佔用的內存不會完全丟棄,而是作爲Cache 緩存在Linux 系統中。當進程運行時,Cache 的代碼指令越多,代碼段產生 page fault 的機率越小,程序的執行速度也越快,這便是Cache 存在的理由。
1.4.3.2 Buffer 緩衝
Buffer 也稱緩衝,是根據Flash 讀寫設計的,把分散的寫操作集中進行,減小Flash 寫的次數,從而提高系統性能。
Cache 和 Buffer 的區別,簡單來說,兩者都是RAM 中的數據,
Buffer 是緩衝即將寫入磁盤的數據, 而 Cache 是緩存從磁盤中讀取出來的數據。
在系統內存不足時,Linux 內核會將一部分 Cache 和 Buffer 釋放,供進程使用,因此Cache 和Buffer 對於進程的內存使用並沒有影響。
可以通過 cat /proc/meminfo 來了解系統中 Cache 和Buffer 的大小:
ciellee@sh:~/work/code/simcom_220c_sop_1028_0426$ cat /proc/meminfo
MemTotal: 16281312 kB
MemFree: 178828 kB
MemAvailable: 13359384 kB
Buffers: 426416 kB
Cached: 13052476 kB
SwapCached: 0 kB
Active: 2841180 kB
Inactive: 12108016 kB
1.4.3.3 內存回收
在 LInux 中,有一個內核進程 kswapd ,其專門負責回收內存。
在 kswapd 中,有2 個閾值: pages_high 和 pages_low ,
當空閒內存頁的數量低於 pages_low 的時候,kswapd 進程就會掃描內存並且每次釋放出 32 個 free pages,直到 free page 的數量到達 pages_high。
(1)kswapd 回收內存有如下原則:
-
如果物理頁面不量 dirty page,就將物理頁面回收。
進程中如下內存可能是非 dirty page頁面:
(1)代碼段:其權限是隻讀屬性,不可能被改寫,所以其所佔的物理內存,都不是dirty page。
(2)數據段:其權限是可讀、可寫,所以其所佔的物理內存可能是 dirty page,也可能不是。
(3)堆段:其權限是可讀、可寫,空容全是通過程序改寫的,所以其所佔用的物理內存,全部是 dirty page。
(4)棧段 和 堆段 相同,其所佔的物理內存,全部是 dirty page。
(5)共享內存,其所佔的物理內存,全部是 dirty page。綜上: 這條規則主要面向的是進程的代碼段 和 未修改的數據段。
如果你做過長時間的系統進程使用分析,就會發現,在進程跑了一段時間後,其代碼所佔的物理內存減少了,這就是代碼段內存回收的結果。 -
如果物理頁面已經修改 並且 可以備份迴文件系統,就調用 pdflush 將內存中的內容 和文件系統進行同步。同步完成後,就可以將內存釋放。
比如,當一個文件在內存中進行修改,pdflush 負責將其寫回磁盤,其主要針對的是下 Buffers。 -
如果物理頁面已經修改但是沒有任何磁盤的備份,就將其寫入 swap 分區。
實際上大部分嵌入式設備很少有交換分區,因此這條規則根本不起作用。
kswapd 在回收內存過程中還有兩個重要的方法:
一是 LMR (Low on memory reclaiming),另一個是 OMK(Out of Memory Killer)。
當分配內存失敗的時候,LMR將會起作用,失敗的原因是 kswapd 不能提供足夠的空閒內存,這個時候LMR 會每次釋放1024 個垃圾頁直到內存分配成功。
當LMR 不能快速釋放內存的時候,OMK 就開始起作用,OMK 會採用一個選擇算法來決定殺死某些進程,選定進程後,就會發送信號 SIGKILL,這就會使內存立即被釋放。
(2)OMK 選擇進程的標準如下:
- 進程佔用大量的內存。
- 進程只會損失少量工作。
- 進程具有低的靜態優先級。
- 進程不屬於 root 用戶。
1.4.3.4 /proc/sys/vm/優化
可以通過修改 /proc/sys/vm 下面的文件,設置一些參數,來影響 LInux內核的一些內存操作行爲。
修改 /proc/ 下文件的方法:
-
# echo 1 > /proc/sys/vm/block_dump
該文件表示是否打開 Block Debug 模式,用於記錄所有的讀寫及 Dirty Block寫回動作。
缺省設置 :0,禁用 Block Debug。 -
# echo 10 > /proc/sys/vm/dirty_background_ratio
該文件表示髒數據到達系統整體內存的百分比,此時觸發 pdflush 進程把髒數據寫回磁盤。
缺省設置:10, 10% -
# echo 3000 > /proc/sys/vm/dirty_expire_centisece
該文件表示如果髒數據在內存中駐留時間超過該值,pdflush 進程在下一次將把這些數據寫回磁盤。
缺省設置:3000, (1/100)s -
# echo 40 > /proc/sys/vm/dirty_ratio
該文件表示如果進程產生的髒數據到達系統整體內存的百分比,此時進程自行把髒數據寫回磁盤。
缺省設置:40, 40% -
# echo 500 > /proc/sys/vm/dirty_writeback_centisecs
該文件表示pdflush 進程週期間隔多久把髒數據寫回磁盤。
缺省設置:500, (1/100)s -
# echo 100 > /proc/sys/vm/vfs_cache_pressure
該文件表示內核回收用於directory 和 inode cache 內存的傾向,
缺省 100 表示內核將根據 pagecache 和 swapcache ,把 directory 和 inode cache 保持在一個合理的百分比;
降低該值於 100,將導致內核傾向於保留 directory 和 inode cache 。
增加該值超過 100 ,將導致內核傾向於回收 directory 和 inode cache 。
缺省設置:100 -
# echo 724 > /proc/sys/vm/min_free_kbytes
該文件表示強制 Linux VM 最低保留多少空閒內存(KB),缺省值:724 -
# cat /proc/sys/vm/nr_pdflush_threads
該文件表示當前正在運行的 pdflush 進程數量,在I/O 負載高的情況下,內核會自動增加更多的pdflush 進程,缺省值:2(只讀) -
# echo 0 > /proc/sys/vm/overcommit_memory
該文件指定了內核針對內存分配的策略,其值可以是:0、1、2,缺省值:0
0:表示內核將檢查是否有足夠的可用內存供應進使用,如果有足夠的可用內存,內存申請允許,否則內存申請失敗,並把錯誤返回給應用進程。
1:表示內核允許分配所有的物理內存,而不管當前的內存狀態如何。
2:表示內核允許分配超進所有物理內存和交換空間總和的內存。 -
# echo 50 > /proc/sys/vm/overcommit_ratio
該文件表示,如果 overcommit_memory = 2 ,可以過載內存的百分比,通過以下公式來計算系統整估可用內存:
系統可分配內存 = 交換空間 + 物理內存 × overcommit_ratio / 100。
缺省設置:50% -
# echo 3 > /proc/sys/vm/page-cluster
該文件表示在寫一次到swap 區的時候寫入的頁面數量,0: 表示1頁, 1:表示2頁,2表示4頁, 缺省3:2^3=8頁。 -
# echo 60 > /proc/sys/vm/swapiness
該文件表示,系統進行交換行爲的程序,數值(0~100)越高,越可能發生磁盤交換,缺省60 -
# echo 0 > /proc/sys/vm/legacy_va_layout
該文件表示是否使用最新的32位共享內存 mmap() 系統調用,Linux 支持的共享內存分配方式包括mmap(), Posix, System VIPC,缺省 0
0:表示使用最新的32位 mmap() 系統調用
1:表示使用 2.4 內核提供的系統調用 -
# cat /proc/sys/vm/nr_hugepages
該文件表示系統保留的 hugetlb 頁數。