内存测量
- 内存管理
- 2023-08-27
- 197热度
- 0评论
系统占用内存
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 '{print1}' | 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 '{print1}' | 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才会增加。