内存测量

系统占用内存

free

旧版本free

$ free
             total       used       free     shared    buffers     cached
Mem:      65960636   63933576   2027060   73392    1602076   32628548
-/+ buffers/cache:       29702952   36257684
Swap:            0          0          0

(1)第一行Mem:内存的使用情况,默认单位是Kb。
- total:系统总共的内存。total=used+free
- used:已经被使用的内存。
- free:剩余还没有被使用的物理内存。
- shared: 多个进程共享的内存。
- buffers:块设备所占的缓存页,包括直接读写块设备、文件系统元数据(metadata)、SupperBlock所使用的缓存页等。
- cached:普通文件数据所占用的缓存页。
(2)第二行-/+ buffers/cache:物理内存缓存统计。
-buffers/cache(used列):29702952,被程序正在使用的缓存内存,等于used-buffers-cached
+buffers/cache(used列):36257684,还可以挪用使用的缓存内存,等于free+buffers+cached
(3)第三行:交换区空间的统计。

新版本free

root@TinaLinux:/# free
              total        used        free      shared  buff/cache   available
Mem:        2022788      98876     1818264    84      105648     1898696
Swap:           0           0           0

第一行Mem:内存的使用情况,默认单位是Kb。
第二行Swap:交换空间的使用情况。
- total:系统总共的内存。total = used+free+buff/cache
- used:已经被使用的内存。
- free:剩余还没有被使用的物理内存。
- shared: 多个进程共享的内存。
- buff:块设备所占的缓存页。
- cache:普通文件数据所占用的缓存页。
- available:还可以被应用程序使用的物理内存大小。available=free+可回收利用的buff/cache
在系统中available才是系统真正可还能申请到的内存。

/proc/meminfo

# cat /proc/meminfo 
MemTotal:        2022788 kB
MemFree:         1818376 kB
MemAvailable:    1898700 kB
Buffers:             436 kB
Cached:            20352 kB
SwapCached:            0 kB
Active:             5104 kB
Inactive:          31432 kB
Active(anon):        116 kB
Inactive(anon):    15716 kB
Active(file):       4988 kB
Inactive(file):    15716 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:         15792 kB
Mapped:             5156 kB
Shmem:                84 kB
KReclaimable:      84752 kB
Slab:             148640 kB
SReclaimable:      84752 kB
SUnreclaim:        63888 kB
KernelStack:        2144 kB
PageTables:          620 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     1011392 kB
Committed_AS:      93972 kB
VmallocTotal:   259653632 kB
VmallocUsed:        4820 kB
VmallocChunk:          0 kB
Percpu:              768 kB
CmaTotal:          65536 kB
CmaFree:           48764 kB
  • MemTotal: 系统当前可用物理内存总量,除去了reserved的内存。
  • MemFree:系统当前剩余空闲物理内存。
  • MemAvailable:系统中可使用的物理内存,包含了可以回收的内存。
  • Buffers:块设备缓存。
  • Cached:普通文件页的缓存。
  • SwapCached:系统有多少匿名页面曾经被交换到交换区过。
  • Active:活动的匿名页面和活动的文件映射页面内存,=Active(anon)+Active(file)。
  • Inactive:不活跃的匿名页面和文件页面。=Inactive(anon)+Inactive(file)。
  • Active(anon):活跃的匿名页面。
  • Inactive(anon): 不活跃的匿名页面。
  • Active(file):活跃的文件页面。
  • Inactive(file): 不活跃的文件页面。
  • Unevictable: 不能回收的页面(LRU_UNEVICTABLE)。
  • Mlocked: 不能被交换(swap)出去的页面。
  • SwapTotal:交换分区的大小。
  • SwapFree: 交换分区空闲的大小。
  • Dirty: 脏页的数量。
  • Writeback: 正在回写的也买你数量。
  • AnonPages:有反向映射的页面,通常是匿名页面并且被映射到用户空间的。
  • Mapped:所有映射到用户地址空间的内容缓存页面。
  • Shmem: 共享内存(tmpfs事先的shmem/devtmpfs等)。
  • KReclaimable: 内核可回收内存,包括可回收的slab和其他可回收的内核页面。
  • Slab:所有slab页面,包括可回收和不可回收。
  • SReclaimable:可回收的slab页面。
  • SUnreclaim: 不可回收的slab页面。
  • KernelStack: 所有进程的内核栈总大小。
  • PageTables: 用于存储页表的页面数量。
  • NFS_Unstable:NFS中,发给服务器但是还没有写入磁盘的页面。
  • Bounce: 针对智能访问低端内存的设备,当DMA分配到高端内存时,分配要给低端临时buffer用于复制处理。
  • WritebackTmp:回写过程中使用的临时缓存
  • CommitLimit:
  • Committed_AS:
  • VmallocTotal:vmalloc区域总大小。
  • VmallocUsed:vmalloc区域使用的内存大小。
  • VmallocChunk:vmalloc可用的连续最大块大小。
  • Percpu:percpu机制使用的页面。
  • CmaTotal:CMA机制使用的总内存。
  • CmaFree:CMA机制剩余空用内存。

1.Linux内核的内存用了多少?Slab+VmallocUsed+PageTables+KernelStack+Bounce。
2.用户内存用了多少内存?
(1)LRU视角:active+inactive+unevicatable = 5104+31432+0= 36536KB
(2)缓存视角(swapcache=0):Cached+Buffers +AnonPages= 20352+436+15792=36580KB,cached和buffers内存并不是都使用完成了,这种统计方式往往大于实际使用内存。
观察内存泄露的时候重点看:Memfree、Slab的变化。

/proc/zoneinfo

zoneinfo显示了当前系统所有内存管理区的信息,可以分为以下几个部分。
1.当前内存节点的内存统计信息
Node 0,zone DMA:第0个节点,DMA区域的总体信息。

2.当前内存管理区的总信息

节点0,zone区域。
- pages free:内存管理区中空闲的页面数量。
- min:警戒水位的页面数量。
- low:低水位的页面数量。
- high:高水位的页面数量。
- spanned:内存管理区总的页面数量,包含空洞。
- present:内存管理区总的可用页面数量,不包含空洞。
- managed:被伙伴系统管理的页面数量。
- protection:管理区中预留内存的页面数量。分别是预留给DMA,DMA32,NORMAL,HIGH。
3.每个CPU内存分配器的信息per_cpu_pageset

pagesets:表示每个CPU内存分配器中每个CPU缓存的页面信息。
- count: 在该CPU内存区域上已经分配的页面数量。
- high:页面回收的空闲页面数量的水位线,如果cpu上的页面数量超过该值,需要退还给zone。
- batch:如果缓存中没有页面了,一次性中zone中获取batch个页面。。
- vm stats threshold: 某个进程虚拟内存使用量超过该阈值时,内核将在/proc/PID/smaps打印进程的详细内存映射信息。
zoneinfo节点重点可以看一下各区域min/low/high的水位值,可以通过/proc/sys/vm/min_free_kbytes调整min值。

用户进程占用内存

/proc/pid/status

# cat /proc/1151/status 
Name:   wifi_daemon
Umask:  0022
State:  S (sleeping)
Tgid:   1151
Ngid:   0
Pid:    1151
PPid:   1
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0
FDSize: 64
Groups:  
VmPeak:   239316 kB
VmSize:   239316 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:      1172 kB
VmRSS:      1172 kB
RssAnon:             532 kB
RssFile:             640 kB
RssShmem:              0 kB
VmData:    33540 kB
VmStk:       132 kB
VmExe:        36 kB
VmLib:      7560 kB
VmPTE:        76 kB
VmSwap:        0 kB
CoreDumping:    0
THP_enabled:    0
Threads:        3
SigQ:   0/7639
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000001000
SigCgt: 0000000180000000
CapInh: 0000000000000000
CapPrm: 000001ffffffffff
CapEff: 000001ffffffffff
CapBnd: 000001ffffffffff
CapAmb: 0000000000000000
NoNewPrivs:     0
Seccomp:        0
Seccomp_filters:        0
Speculation_Store_Bypass:       not vulnerable
SpeculationIndirectBranch:      unknown
Cpus_allowed:   ff
Cpus_allowed_list:      0-7
voluntary_ctxt_switches:        12
nonvoluntary_ctxt_switches:     0
  • VmPeak: 进程使用的最大虚拟内存,通常等于进程内存描述符号mm->total_vm。
  • VmSize:进程使用的虚拟内存,等于mm->total_vm。
  • VmLck:记录所有用户或内核锁定的内存,主要是mlock的内存,系统回收内存时,不会优先回收这部分内存。
  • VmPin:进程固定在内存的虚拟地址空间大小,记录了无法被换出到磁盘的页面数量。
  • VmHWM:进程使用的最大物理内存,包括进程使用的匿名页面、文件映射页面以及共享内存页面大小总和。
  • VmRSS: 进程使用的最大物理内存,通常等于VmHMM。
  • RssAnon: 进程使用的匿名页面大小。
  • RssFile: 进程使用的文件页面大小。
  • RssShmem: 进程使用的共享内存页面大小。
  • VmData:进程私有数据段占用内存大小。
  • VmStk:进程用户栈占用内存大小。
  • VmExe:进程代码段占用大小。
  • VmLib:进程共享库占用大小。
  • VmPTE:进程占用的页表大小。
  • VmSwap: 进程使用巨型页的大小。

/proc/pid/smaps

7fac964000-7fac9b8000 r-xp 00000000 b3:07 403    /lib/libwifimg-v2.0.so
Size:                336 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB
Rss:                 240 kB
Pss:                 240 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:       240 kB
Private_Dirty:         0 kB
Referenced:          136 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
FilePmdMapped:         0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB
SwapPss:               0 kB
Locked:                0 kB
THPeligible:    0
VmFlags: rd ex mr mw me 
  • 7fac964000-7fac9b8000 r-xp 00000000 b3:07 403 /lib/libwifimg-v2.0.so: 7fac964000-7fac9b8000虚拟内存段的开始和结束位置,表示一个VMA。 r-xp表示该VMA是可读、可执行、私有。00000000虚拟内存段其实地址对应映射文件中以页为单位的偏移量。
  • size:虚拟内存空间大小。不是实际物理内存的分配大小,对应的是VMA的内存大小,内存总是会延迟分配。
  • Rss:实际分配的内存,包括其他进程的共享内存Rss=Shared_Clean+Shared_Dirty+Private_Clean+Private_Dirty。
  • Pss:平摊计算后的实际物理内存使用。共享部分按比例均分+Private_xx部分。
  • Private_Dirty:进程独占的脏页面大小。
  • Private_Clean:进程独占干净页面大小。
  • USS:等于Private_Dirty+Private_Clean,通常用来表示进程独占的物理内存大小,去掉与其他共享内存部分。

Rss计算:

cat /proc/pid/smaps | awk '/^Rss/ {sum += $2} END {print sum}'

Pss计算

cat /proc/pid/smaps | awk '/^Pss/ {sum += $2} END {print sum}'

Uss计算

cat /proc/pid/smaps | awk '/^Pss/ {sum += $2} END {print sum}'

进程1001:
VSS=1+2+3
RSS=4+5+6
PSS=4/2+5+6
USS=5+6
看进程是否有内存泄露,可以优先看USS(Private_Dirty+Private_Clean)是否有增长。

pmap -x [pid]



- Address:虚拟地址起始地址
- Kbytes:内存块占用虚拟内存大小,单位KB。
- PSS:进程使用的物理内存大小(贡献内存按比例分配的大小),单位KB。
- Dirty:脏内存大小,指进程修改过的页面大小,单位KB。
- Swap:被交换到磁盘上的内存大小,单位KB。
- Mode:显示内存段的权限属性。
- Mapping:显示内存段对应的文件或库名。
- Total:汇总内存区域的虚拟内存大小,单位KB。
在linux物理内存中,每个页面有一个dirty的标志,如果该页面被改写了,我们称之位dirty page。总的来说,所有非dirty page的物理页面都可以被回收。进程中各个段的dirty page情况。

内核占用内存

/proc/meminfo

内核占用内存:Slab+VmallocUsed+PageTables+KernelStack+Bounce。

/proc/pagetypeinfo

Page block order:10,支持最高阶order,这里是10,表示内存大小一块为2^10页面。
Pages per block:最高阶一块内存需要的页面数量,等于2^10=1024,即page block大小为1024*4K=4MB。
Unmovable order=1的数量有87个,也就是2^1个页面组成的内存块有87个。
Movable 454:表示在DMA区域,movable类型的page block的数量为454。(按照最高阶计算oder = 2^10的页面组成的内存块)。

小结

内存统计

#!/bin/sh

while true; do
    #1计算系统总共内存及剩余内存
    TOTAL_MEM=(grep MemTotal /proc/meminfo | awk '{print2}')
    FREE_MEM=(grep MemFree /proc/meminfo | awk '{print2}')

    #2计算内核占用内存
    SLAB=(grep Slab /proc/meminfo | awk '{print2}')
    VMALLOC=(grep VmallocUsed /proc/meminfo | awk '{print2}')
    PAGETABLE=(grep PageTables /proc/meminfo | awk '{print2}')
    KERNELSTACK=(grep KernelStack /proc/meminfo | awk '{print2}')
    KERNEL_MEM=((SLAB + VMALLOC + PAGETABLE + KERNELSTACK))

#3计算用户USS/PSS/RSS占用内存
    CURRENT_USER=(id -u)
    TOTAL_USS=0
    TOTAL_PSS=0
    TOTAL_RSS=0
    for pid in (ls /proc/ | grep "^[0-9]*"); do
        if [ -f "/proc/{pid}/status" ]; then
            username=(awk '/^Uid:/{printf 2}' "/proc/{pid}/status")
            if [ "username" = "CURRENT_USER" ]; then
                name=(awk '/^Name:/{print2}' "/proc/{pid}/status")
                mem1=0
                mem1=(cat /proc/{pid}/smaps | awk '/^Private/ {sum +=2} END {print sum}')
                mem2=0
                mem2=(cat /proc/{pid}/smaps | awk '/^Pss/ {sum += 2} END {print sum}')
                mem3=0
                mem3=(cat /proc/{pid}/smaps | awk '/^Rss/ {sum +=2} END {print sum}')
            fi
            TOTAL_USS=((TOTAL_USS + mem1))
            TOTAL_PSS=((TOTAL_PSS + mem2))
            TOTAL_RSS=((TOTAL_RSS + mem3))
        fi
    done

    echo "total_mem:TOTAL_MEM KB, free_mem:FREE_MEM KB"
    echo "slab:SLAB KB, vmalloc:VMALLOC KB, pagetable:PAGETABLE KB, kernel_stack:KERNELSTACK KB"
    echo  "Kernel_mem:KERNEL_MEM KB, USS:TOTAL_USS KB, PSS:TOTAL_PSS KB, RSS:$TOTAL_RSS KB"
    sleep 1
done

查询占用较多内存的进程

#!/bin/sh
CURRENT_USER=(id -u)
TOTAL_USS=0
TOTAL_PSS=0
TOTAL_RSS=0
for pid in(ls /proc/ | grep "^[0-9]*"); do
    if [ -f "/proc/{pid}/status" ]; then
        username=(awk '/^Uid:/{printf2}' "/proc/{pid}/status")
        if [ "username" = "CURRENT_USER" ]; then
            name=(awk '/^Name:/{print 2}' "/proc/{pid}/status")
            mem1=0
            mem1=(cat /proc/{pid}/smaps | awk '/^Private/ {sum += 2} END {print sum}')
            mem2=0
            mem2=(cat /proc/{pid}/smaps | awk '/^Pss/ {sum +=2} END {print sum}')
          mem3=0
            mem3=(cat /proc/{pid}/smaps | awk '/^Rss/ {sum += 2} END {print sum}')
            echo "pid USS:mem1, PSS:mem2, RSS:$mem3"
         fi
fi
done

进程内存监测

#!/bin/sh

count=0
pid=1
while true; do
 let "count++"
 #计算USS,RSS,PSS
 mem1=(cat /proc/pid/smaps | awk '/^Private/ {sum +=2} END {print sum}')
 mem2=(cat /proc/pid/smaps | awk '/^Rss/ {sum += 2} END {print sum}')
 mem3=(cat /proc/pid/smaps | awk '/^Pss/ {sum +=2} END {print sum}')
 #计算Dirty,进程虚拟内存
 Dirty=(pmap -xpid | awk '/^total/{dirty=4}END{print dirty}')
 VSS=(pmap -x pid | awk '/^total/{VSS=2}END{print VSS}')
 #计算系统使用内存,剩余内存
 used=(free | awk '/^Mem/{used=3}END{print used}')
 free=(free | awk '/^Mem/{free=4}END{print free}')
 #计算堆空间虚拟内存
start_addr=(cat /proc/pid/maps | grep "
" | awk '{print 1}' | cut -d'-' -f1) end_addr=(cat /proc/pid/maps | grep "
" | awk '{print
1}' | cut -d'-' -f2) start_addr=(printf " end_addr=(printf " heap_vs=(exprend_addr - start_addr) heap_vs=(expr heap_vs / 1024) #计算栈空间虚拟内存 start_addr=(cat /proc/pid/maps | grep "
" | awk '{print
1}' | cut -d'-' -f1) end_addr=(cat /proc/pid/maps | grep "
" | awk '{print 1}' | cut -d'-' -f2) start_addr=(printf " end_addr=
(printf " stack_vs=
(expr end_addr -start_addr) stack_vs=(exprstack_vs / 1024) echo "count:count,USS:mem1 KB,VSS:VSS KB, RSS:mem2 KB,PSS:mem3 KB,Dirty:Dirty KB,heapvs:heap_vs KB,stackvs:stack_vs KB,used:used KB,free:free KB" sleep 1 done

测试代码,可配合脚本观察

int main(int argc, char *argv[])
{
    char *ptr1;
    char *ptr2;
    char *ptr3;
    char *ptr4;
    char *ptr5;
    struct mallinfo m_info;
    int size_kb = 65;

    getchar();

    mallopt(M_TRIM_THRESHOLD, 1024 * 128);

    printf("malloc ptr[0] 
    ptr1 = (char *)malloc(1048 * size_kb);
    //memset(ptr1,24,1048 * size_kb); //memset不会导致USS增加,可能是由于值是一样的.
    //memset(ptr1,25,1048 * size_kb); 即使用两次memset设置不同值也不会增加USS
    for (int i = 0; i < 1048 * size_kb; i++) {
        ptr1[i] = i 
    }
    getchar();

    printf("malloc ptr[1] 
    ptr2 = (char *)malloc(1048 * size_kb);
    //memset(ptr2,25,1048 * size_kb);
    for (int i = 0; i < 1048 * size_kb; i++) {
        ptr2[i] = i 
    }
    getchar();

    printf("malloc ptr[2] 
    ptr3 = (char *)malloc(1048 * size_kb);
    //memset(ptr3,26,1048 * size_kb);
    for (int i = 0; i < 1048 * size_kb; i++) {
        ptr3[i] = i 
    }
getchar();

    printf("malloc ptr[3] 
    ptr4 = (char *)malloc(1048 * size_kb);
    //memset(ptr4,27,1048 * size_kb);
    for (int i = 0; i < 1048 * size_kb; i++) {
        ptr4[i] = i 
    }
    getchar();

    printf("malloc ptr[4] 
    ptr5 = (char *)malloc(1048 * size_kb);
    //memset(ptr5,28,1048 * size_kb);
    for (int i = 0; i < 1048 * size_kb; i++) {
        ptr5[i] = i 
    }
    getchar();


    printf("free ptr[0] 
    free(ptr1);

    getchar();
    printf("free ptr[1] 
    free(ptr2);

    getchar();
    printf("free ptr[2] 
    free(ptr3);

    getchar();
    printf("free ptr[3] 
    free(ptr4);

    getchar();
    printf("free ptr[4] 
    free(ptr5);

    getchar();
    return 0;
}

操作系统对于小块内存的管理方式,如 Linux 内核中的延迟映射(Lazy Mapping)或零页复制(Zero Page COW)等技术所导致的。在某些情况下,操作系统可能会推迟实际的物理页面映射,直到首次访问相应的内存位置。这意味着即使您访问和修改了分配的内存,实际的物理页面映射可能仍然被推迟。即使使用memset修改了内存,但是可能也不会进行映射,通常可能只有在内存被修改为不同内容时,才会进行实际物理页面映射。而只有做了物理页面映射,进程的USS才会增加。