脚本内容:
# 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内核的内存分配机制问题。下面这篇博客介绍的很多。
https://blog.csdn.net/gfgdsg/article/details/42709943
实操: 测试程序, 直接用 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>) 来修改这个临界值。