最近在学习Linux系统的内存管理,小白一枚,零散从网上收集的一些笔记如下:
/proc目录提供了很多工具给我们查看当前内存情况
1. /proc/meminfo是什么
- $cat /proc/meminfo
- MemTotal: 2052440 kB //总内存
- MemFree: 50004 kB //空闲内存
- Buffers: 19976 kB //给文件的缓冲大小
- Cached: 436412 kB //高速缓冲存储器(http://baike.baidu.com/view/496990.htm)使用的大小
- SwapCached: 19864 kB //被高速缓冲存储用的交换空间大小
- Active: 1144512 kB //活跃使用中的高速缓冲存储器页面文件大小
- Inactive: 732788 kB //不经常使用的高速缓冲存储器页面文件大小
- Active(anon): 987640 kB //anon:不久
- Inactive(anon): 572512 kB
- Active(file): 156872 kB
- Inactive(file): 160276 kB
- Unevictable: 8 kB
- Mlocked: 8 kB
- HighTotal: 1177160 kB //The total and free amount of memory, in kilobytes, that is not directly mapped into kernel space.
- HighFree: 7396 kB // The HighTotal value can vary based on the type of kernel used.
- LowTotal: 875280 kB // The total and free amount of memory, in kilobytes, that is directly mapped into kernel space. used.
- LowFree: 42608 kB //The LowTotal value can vary based on the type of kernel
- SwapTotal: 489940 kB //交换空间总大小
- SwapFree: 450328 kB //空闲交换空间
- Dirty: 104 kB //等待被写回到磁盘的大小
- Writeback: 0 kB //正在被写回的大小
- AnonPages: 1408256 kB //未映射的页的大小
- Mapped: 131964 kB //设备和文件映射的大小
- Slab: 37368 kB //内核数据结构缓存的大小,可减少申请和释放内存带来的消耗
- SReclaimable: 14164 kB //可收回slab的大小
- SUnreclaim: 23204 kB //不可收回的slab的大小23204+14164=37368
- PageTables: 13308 kB //管理内存分页的索引表的大小
- NFS_Unstable: 0 kB //不稳定页表的大小
- Bounce: 0 kB //bounce:退回
- WritebackTmp: 0 kB //
- CommitLimit: 1516160 kB
- Committed_AS: 2511900 kB
- VmallocTotal: 122880 kB //虚拟内存大小
- VmallocUsed: 28688 kB //已经被使用的虚拟内存大小
- VmallocChunk: 92204 kB
- HugePages_Total: 0 //大页面的分配
- HugePages_Free: 0
- HugePages_Rsvd: 0
- HugePages_Surp: 0
- Hugepagesize: 2048 kB
- DirectMap4k: 10232 kB
- DirectMap2M: 899072 kB
Active: 1308168 kB = Active(anon): 1010416 kB + Active(file): 297752 kB
然后 Active(anon) 表示 anonymous pages,Active(file)表示 file-backed pages (inactive 同理)
Active/inactive memory 是针对用户进程所占用的内存而言的,内核占用的内存(包括 slab )不在其中。
ACTIVE_ANON 和 ACTIVE_FILE,分别表示 anonymous pages 和 file-backed pages。用户进程的内存页分为两种:与文件关联的内存(比如程序文件、数据文件所对应的内存页)和与文件无关的内存(比如进程的堆栈,用 malloc 申请的内存),前者称为 file-backed pages,后者称为 anonymous pages。File-backed pages 在发生换页(page-in 或 page-out)时,是从它对应的文件读入或写出; anonymous pages 在发生换页时,是对交换区进行读 /写操作。
比较好文章链接:单纯说 /proc/meminfo 里的各项数据 http://linuxperf.com/?p=142
2.ps命令
常用的就是ps -ef和pf aux
ps -ef 是用标准的格式显示进程的。其格式如下:
UID | PID | PPID | C | STIME | TTY | CMD |
用户ID | 进程的ID | 父进程ID | 进程占用CPU的百分比 | 进程启动的时间 | 该进程在那个终端上运行。 若与终端无关,则显示? 若为pts/0等,则表示由网络连接主机进程。 | 命令的名称和参数 |
ps aux 是用BSD的格式来显示。其格式如下:
USER | PID | %CPU | %MEM | VSZ | RSS | TTY | STAT | START | TIME | COMMAND |
用户名 | 同上 | 进程占用的CPU百分比 | 占用内存的百分比 | 该进程使用的虚拟內存量(KB) | 该进程占用的固定內存量(KB) (驻留中页的数量) | 同上 | 进程的状态 | 同上 | 该进程实际使用CPU运行的时间 | 同上 |
其中STAT状态位常见的状态字符有
D //无法中断的休眠状态(通常 IO 的进程);
R //正在运行可中在队列中可过行的;
S //处于休眠状态;
T //停止或被追踪;
W //进入内存交换 (从内核2.6开始无效);
X //死掉的进程 (基本很少见);
Z //僵尸进程;
< //优先级高的进程
N //优先级较低的进程
L //有些页被锁进内存;
s //进程的领导者(在它之下有子进程);
l //多线程,克隆线程(使用 CLONE_THREAD, 类似 NPTL pthreads);
+ //位于后台的进程组;
3.free 命令显示系统内存的使用情况,包括物理内存、交换内存(swap)和内核缓冲区内存。
$ free -h -s 3
上面的命令每隔 3 秒输出一次内存的使用情况,直到你按下 ctrl + c。
# free
Mem 行(第二行)是内存的使用情况。
Swap 行(第三行)是交换空间的使用情况。
total 列显示系统总的可用物理内存和交换空间大小。
used 列显示已经被使用的物理内存和交换空间。表示总计分配给缓存(包含buffers 与cache )使用的数量,但其中可能部分缓存并未实际使用。
free 列显示还有多少物理内存和交换空间可用使用。
shared 列显示被共享使用的物理内存大小。
buff/cache 列显示被 buffer 和 cache 使用的物理内存大小。
available 列显示还可以被应用程序使用的物理内存大小。
used2:实际使用的buffers 与cache 总量,也是实际使用的内存总量。
free2:未被使用的buffers 与cache 和未被分配的内存之和,这就是系统当前实际可用内存。
total = used + free
total = used2 + free2
used = buffers + cached + used2
free2 = buffers + cached + free
buff/cache
A buffer is something that has yet to be "written" to disk.
A cache is something that has been "read" from the disk and stored for later use.
先来提一个问题: buffer 和 cache 应该是两种类型的内存,但是 free 命令为什么会把它们放在一起呢?
buffer 在操作系统中指 buffer cache, 中文一般翻译为 "缓冲区"。要理解缓冲区,必须明确另外两个概念:"扇区" 和 "块"。扇区是设备的最小寻址单元,也叫 "硬扇区" 或 "设备块"。块是操作系统中文件系统的最小寻址单元,也叫 "文件块" 或 "I/O 块"。每个块包含一个或多个扇区,但大小不能超过一个页面,所以一个页可以容纳一个或多个内存中的块。当一个块被调入内存时,它要存储在一个缓冲区中。每个缓冲区与一个块对应,它相当于是磁盘块在内存中的表示(下图来自互联网):
注意,buffer cache 只有块的概念而没有文件的概念,它只是把磁盘上的块直接搬到内存中而不关心块中究竟存放的是什么格式的文件。
cache 在操作系统中指 page cache,中文一般翻译为 "页高速缓存"。页高速缓存是内核实现的磁盘缓存。它主要用来减少对磁盘的 I/O 操作。具体地讲,是通过把磁盘中的数据缓存到物理内存中,把对磁盘的访问变为对物理内存的访问。页高速缓存缓存的是内存页面。缓存中的页来自对普通文件、块设备文件(这个指的就是 buffer cache 呀)和内存映射文件的读写。
页高速缓存对普通文件的缓存我们可以这样理解:当内核要读一个文件(比如 /etc/hosts)时,它会先检查这个文件的数据是不是已经在页高速缓存中了。如果在,就放弃访问磁盘,直接从内存中读取。这个行为称为缓存命中。如果数据不在缓存中,就是未命中缓存,此时内核就要调度块 I/O 操作从磁盘去读取数据。然后内核将读来的数据放入页高速缓存中。这种缓存的目标是文件系统可以识别的文件(比如 /etc/hosts)。
页高速缓存对块设备文件的缓存就是我们在前面介绍的 buffer cahce。因为独立的磁盘块通过缓冲区也被存入了页高速缓存(缓冲区最终是由页高速缓存来承载的)。
到这里我们应该搞清楚了:无论是缓冲区还是页高速缓存,它们的实现方式都是一样的。缓冲区只不过是一种概念上比较特殊的页高速缓存罢了。
那么为什么 free 命令不直接称为 cache 而非要写成 buff/cache? 这是因为缓冲区和页高速缓存的实现并非天生就是统一的。在 linux 内核 2.4 中才将它们统一。更早的内核中有两个独立的磁盘缓存:页高速缓存和缓冲区高速缓存。前者缓存页面,后者缓存缓冲区。当你知道了这些故事之后,输出中列的名称可能已经不再重要了。
free 与 available
在 free 命令的输出中,有一个 free 列,同时还有一个 available 列。这二者到底有何区别?
free 是真正尚未被使用的物理内存数量。至于 available 就比较有意思了,它是从应用程序的角度看到的可用内存数量。
Linux 内核为了提升磁盘操作的性能,会消耗一部分内存去缓存磁盘数据,就是我们介绍的 buffer 和 cache。所以对于内核来说,buffer 和 cache 都属于已经被使用的内存。当应用程序需要内存时,如果没有足够的 free 内存可以用,内核就会从 buffer 和 cache 中回收内存来满足应用程序的请求。所以从应用程序的角度来说,available = free + buffer + cache。请注意,这只是一个很理想的计算方式,实际中的数据往往有较大的误差。
交换空间(swap space)
swap space 是磁盘上的一块区域,可以是一个分区,也可以是一个文件。所以具体的实现可以是 swap 分区也可以是 swap 文件。当系统物理内存吃紧时,Linux 会将内存中不常访问的数据保存到 swap 上,这样系统就有更多的物理内存为各个进程服务,而当系统需要访问 swap 上存储的内容时,再将 swap 上的数据加载到内存中,这就是常说的换出和换入。交换空间可以在一定程度上缓解内存不足的情况,但是它需要读写磁盘数据,所以性能不是很高。
现在的机器一般都不太缺内存,如果系统默认还是使用了 swap 是不是会拖累系统的性能?理论上是的,但实际上可能性并不是很大。并且内核提供了一个叫做 swappiness 的参数,用于配置需要将内存中不常用的数据移到 swap 中去的紧迫程度。这个参数的取值范围是 0~100,0 告诉内核尽可能的不要将内存数据移到 swap 中,也即只有在迫不得已的情况下才这么做,而 100 告诉内核只要有可能,尽量的将内存中不常访问的数据移到 swap 中。在 ubuntu 系统中,swappiness 的默认值是 60。如果我们觉着内存充足,可以在 /etc/sysctl.conf 文件中设置 swappiness:
vm.swappiness=10
如果系统的内存不足,则需要根据物理内存的大小来设置交换空间的大小。
4.Linux中常用内存分配函数的异同点
用户/内核 | API名称 | 物理连续? | 大小限制 | 单位 | 场景 | |
用户空间 | malloc/calloc/realloc/free | 不保证 | 堆申请 | 字节 | calloc初始化为0;realloc改变内存大小。 | |
alloca | 栈申请 | 字节 | 向栈申请内存 | |||
mmap/munmap | 将文件利用虚拟内存技术映射到内存中去。 | |||||
brk、sbrk | 虚拟内存到内存的映射。sbrk(0)返回program break地址,sbrk调整对的大小。 | |||||
内 核 空 间 | vmalloc/vfree | 虚拟连续 物理不定 | vmalloc区大小限制 | 页 VMALLOC区域 | 可能睡眠,不能从中断上下文中调用,或其他不允许阻塞情况下调用。 VMALLOC区域vmalloc_start~vmalloc_end之间,vmalloc比kmalloc慢,适用于分配大内存。 | |
slab | kmalloc/kcalloc/krealloc/kfree | 物理连续 | 64B-4MB (随slab而变) | 2^order字节 Normal区域 | 大小有限,不如vmalloc/malloc大。 最大/小值由KMALLOC_MIN_SIZE/KMALLOC_SHIFT_MAX,对应64B/4MB。 从/proc/slabinfo中的kmalloc-xxxx中分配,建立在kmem_cache_create基础之上。 | |
kmem_cache_create | 物理连续 | 64B-4MB | 字节大小,需对齐 Normal区域 | 便于固定大小数据的频繁分配和释放,分配时从缓存池中获取地址,释放时也不一定真正释放内存。通过slab进行管理。 | ||
伙伴系统 | __get_free_page/__get_free_pages | 物理连续 | 4MB(1024页) | 页 Normal区域 | __get_free_pages基于alloc_pages,但是限定不能使用HIGHMEM。 | |
alloc_page/alloc_pages/free_pages | 物理连续 | 4MB | 页 Normal/Vmalloc都可 | CONFIG_FORCE_MAX_ZONEORDER定义了最大页面数2^11,一次能分配到的最大页面数是1024。 |
5.procrank命令
procrank是android system/xbin工具,能够列出各进程占用内存情况,从大到小排列
- root@msm8952_64:/ # procrank
- PID Vss Rss Pss Uss cmdline
- 1628 2275600K 116260K 64803K 56572K system_server
- 4740 1562536K 106804K 54520K 45168K com.android.systemui
- 5325 1570524K 91976K 40356K 30108K com.android.launcher3
一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS
Vss与PS的VSIZE相同是单个进程全部可访问的地址空间,其大小包括可能还尚未在内存中驻留的部分。比如地址空间已经被 malloc 分配,但是还没有实际写入。对于确定单个进程实际内存使用大小, VSS 用处不大
RSS 是单个进程实际占用的内存大小。RSS 易被误导的原因在于, 它包括了该进程所使用的所有共享库的全部内存大小。对于单个共享库, 尽管无论多少个进程使用,实际该共享库只会被装入内存一次。对于单个进程的内存使用大小, RSS 不是一个精确的描述。
PSS 不同于RSS,它只是按比例包含其所使用的共享库大小。例如, 三个进程使用同一个占用 30 内存页的共享库。 对于三个进程中的任何一个,PSS 将只包括 10 个内存页。PSS 是一个非常有用的数字,因为系统中全部进程以整体的方式被统计, 对于系统中的整体内存使用是一个很好的描述。如果一个进程被终止, 其PSS 中所使用的共享库大小将会重新按比例分配给剩下的仍在运行并且仍在使用该共享库的进程。此种计算方式有轻微的误差,因为当某个进程中止的时候, PSS 没有精确的表示被返还给整个系统的内存大小。
USS 是单个进程的全部私有内存大小。亦即全部被该进程独占的内存大小。USS 是一个非常非常有用的数字, 因为它揭示了运行一个特定进程的真实的内存增量大小。如果进程被终止, USS 就是实际被返还给系统的内存大小。USS 是针对某个进程开始有可疑内存泄露的情况,进行检测的最佳数字。
VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
6.top命令
VIRT:virtual memory usage 虚拟内存
1、进程“需要的”虚拟内存大小,包括进程使用的库、代码、数据等
2、假如进程申请100m的内存,但实际只使用了10m,那么它会增长100m,而不是实际的使用量
RES:resident memory usage 常驻内存
1、进程当前使用的内存大小,但不包括swap out
2、包含其他进程的共享
3、如果申请100m的内存,实际使用10m,它只增长10m,与VIRT相反
4、关于库占用内存的情况,它只统计加载的库文件所占内存大小
SHR:shared memory 共享内存
1、除了自身进程的共享内存,也包括其他进程的共享内存
2、虽然进程只使用了几个共享库的函数,但它包含了整个共享库的大小
3、计算某个进程所占的物理内存大小公式:RES – SHR
4、swap out后,它将会降下来
DATA
1、数据占用的内存。如果top没有显示,按f键可以显示出来。
2、真正的该程序要求的数据空间,是真正在运行中要使用的。