1. 程式人生 > >[從0到1搭嵌入式工程]計算程序佔用的記憶體大小

[從0到1搭嵌入式工程]計算程序佔用的記憶體大小

指令碼內容:

    # cat get_process_mem.sh 
    pid=$(ps|grep myprocess|grep -v grep|awk '{print $1}')
    cat /proc/$pid/status|grep VmRSS|awk '{print $2}'

在程式程式碼中,可以使用 system呼叫指令碼,獲取到自身佔用了多少記憶體, 如果佔用記憶體過大,可以選擇重啟自己。

先根據名字獲取到程序的PID,然後根據 /proc/$pid/status 中的程序資訊,獲取到VmRSS 這個佔用實體記憶體大小。

cat /proc/398/status 
Name:   myprocess
State:  S (sleeping)
Tgid:   398
Pid:    398
PPid:   379
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0
FDSize: 32
Groups:
VmPeak:    54004 kB
VmSize:    54004 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:      2856 kB
VmRSS:      2496 kB
VmData:    27136 kB
VmStk:       136 kB
VmExe:      4284 kB
VmLib:     17792 kB
VmPTE:        28 kB
VmSwap:        0 kB
Threads:        3
SigQ:   0/829
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000001000
SigCgt: 0000000180004006
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
Cpus_allowed:   1
Cpus_allowed_list:      0
voluntary_ctxt_switches:        18087
nonvoluntary_ctxt_switches:     32038

VmRSS:程序實際佔用實體記憶體大小;

VmSize:任務虛擬地址空間的大小。

計算實體記憶體佔用大小,本質是linux核心的記憶體分配機制問題。下面這篇部落格介紹的很多。

實操: 測試程式, 直接用 cat /proc/$PID/status  檢視

204KB   空程式不分配記憶體, 根據記憶體分配機制,程式區和文字區佔用的記憶體

208KB  棧上分配 100KB, 不進行初始化,不分配

212KB  棧上分配 100KB, memset 初始化2K,不清楚為什麼會多分

288KB  棧上分配 100KB, memset 初始化80K, 對於棧上的空間,用多少分多少

304KB  棧上分配 100KB, 賦值為{0} 全部進行初始化

404KB  棧上分配 兩個100KB,賦值為{0} 全部進行初始化, 棧上記憶體線性增長

220KB   堆上分配100KB, 不進行初始化, 堆上不使用不分配

220KB   堆上分配100KB, 對前10個位元組進行初始化

240KB   堆上分配100KB, 對前20K個位元組進行初始化, 用多少分多少

316KB   堆上分配100KB, 全部進行初始化

316KB   堆上分配100KB, 全部進行初始化,然後再free,記憶體並沒有被回收

316KB   堆上分配100KB, 全部進行初始化,然後再free,再重新分配100KB,不會增加

324KB   堆上分配100KB, 全部進行初始化,然後再free,再重新分配110KB, 會增加差值

416KB   堆上分配200KB, 全部進行初始化

416KB   堆上分配200KB, 全部進行初始化,然後再free, 記憶體還是沒有被回收(說好的>128K,unmap會一起回收虛擬和實體記憶體的? 分配800KB再次實驗, free之後是回收的,回收閾值是可以mallopt設定的,但是沒有找到設定的地方在哪裡)

416KB   堆上分配兩個100KB, 全部進行初始化

416KB   堆上分配兩個100KB, 全部進行初始化,兩個全部釋放,記憶體還是沒有被回收(說好的>128K,記憶體緊縮會回收物理記憶體的? )

204KB   全域性區分配100KB,不進行初始化

204KB   全域性區分配100KB,賦值為{0}初始化,沒有分配實體記憶體

208KB   全域性區分配100KB,賦值為{1}初始化,分配出一個頁的實體記憶體

304KB   全域性區分配100KB,在main中使用memset 0x00進行初始化

Linux 的虛擬記憶體管理有幾個關鍵概念: 

1、每個程序都有獨立的虛擬地址空間,程序訪問的虛擬地址並不是真正的實體地址; 
2、虛擬地址可通過每個程序上的頁表(在每個程序的核心虛擬地址空間)與實體地址進行對映,獲得真正實體地址; 
3、如果虛擬地址對應實體地址不在實體記憶體中,則產生缺頁中斷,真正分配實體地址,同時更新程序的頁表,一個頁表大小為4KB;如果此時實體記憶體已耗盡,則根據記憶體替換演算法淘汰部分頁面至物理磁碟中。

不管是堆上、棧上還是全域性區, Linux記憶體管理的基本思想之一,是隻有在真正訪問一個地址的時候才建立這個地址的物理對映。

堆上佔用的實體記憶體,在free之後,需要滿足記憶體緊縮條件才會釋放出來。因為高地址釋放之後,低地址才能釋放,不會立即收回實體記憶體,當前面的被free了,後面的還佔著,就會產生記憶體碎片。這些記憶體碎片在下一次分配時可重用,不需要再分實體記憶體給他。

對於大記憶體(>128KB),會被分配到堆和棧之間獨立的區域,在free時,連帶的實體記憶體也會釋放掉。

記憶體緊縮和unmap能夠起到回收物理記憶體的作用,同時也能避免實體記憶體被小記憶體分配頻繁的分配和釋放。 但是如果程式中有頻繁的大記憶體分配,因為總是被緊縮,或者unmap,再分配時,又會重新分配實體記憶體,導致大量的缺頁中斷,影響到CPU的效能。這時,可以選擇將這兩個功能關掉mallopt(M_MMAP_MAX, 0) 。

上面連結中說的:堆是一個連續空間,並且堆內碎片由於沒有歸還 OS ,如果可重用碎片,再次訪問該記憶體很可能不需產生任何系統呼叫和缺頁中斷,這將大大降低 CPU 的消耗。 因此, glibc 的 malloc 實現中,充分考慮了 sbrk 和 mmap 行為上的差異及優缺點,預設分配大塊記憶體 (128k) 才使用 mmap 獲得地址空間,也可通過 mallopt(M_MMAP_THRESHOLD, <SIZE>) 來修改這個臨界值。