了解Linux 内存使用

原创 2012年03月28日 17:52:05

1. 用户进程内存结构

top 命令了解进程信息,其中包括内存方面的信息。

正在运行的程序,叫进程。每个进程都有完全属于自己的,独立的,不被干扰的内存空间。此空间,被分成几个段(Segment),分别是Text, Data, BSS, Heap, Stack。用户进程内存空间,也是系统内核分配给该进程的VM(虚拟内存),但并不表示这个进程占用了这么多的RAM(物理内存)。这个空间有多大?命令top输出的VIRT值告诉了我们各个进程内存空间的大小(进程内存空间随着程序的执行会增大或者缩小)。你还可以通过/proc/$pid/maps,或者pmap –d 了解某个进程内存空间都分布,比如:

每台主机都维护一个ARP缓存表,可以用arp -a命令查看。

[root@localhost4 vhosts]#  pmap  19254      -x
19254:   nginx: master process /usr/local/app/nginx/sbin/nginx
Address           Kbytes     RSS   Dirty Mode   Mapping
0000000000400000     552     224       0 r-x--  nginx
0000000000689000      72      60      60 rw---  nginx
000000000069b000      56      12      12 rw---    [ anon ]
0000000000950000     652     636     636 rw---    [ anon ]
00000037c3a00000      92      44       0 r-x--  libpthread-2.12.so
00000037c3a17000    2048       0       0 -----  libpthread-2.12.so
00000037c3c17000       4       4       4 r----  libpthread-2.12.so
00000037c3c18000       4       4       4 rw---  libpthread-2.12.so

扩展和设备格式区域
Address: 内存开始地址
Kbytes: 占用内存的字节数(KB)
RSS: 保留内存的字节数(KB)
Dirty: 脏页的字节数(包括共享和私有的)(KB)
Mode: 内存的权限:read、write、execute、shared、private (写时复制)
Mapping: 占用内存的文件、或[anon](分配的内存)、或[stack](堆栈)
Offset: 文件偏移
Device: 设备名 (major:minor)


当fork()或者exec()一个进程的时候,系统内核就会分配一定量的VM给进程,作为进程的内存空间,大小由BSS段,Data段的已定义的全局变量、静态变量、Text段中的字符直接量、程序本身的内存映像等,还有Stack段的局部变量决定。当然,还可以通过malloc()等函数动态分配内存,向上扩大heap。

动态分配与静态分配,二者最大的区别在于:

1. 直到Run-Time的时候,执行动态分配,而在compile-time的时候,就已经决定好了分配多少Text+Data+BSS+Stack。

2.通过malloc()动态分配的内存,需要程序员手工调用free()释放内存,否则容易导致内存泄露,而静态分配的内存则在进程执行结束后系统释放(Text, Data), 但Stack段中的数据很短暂,函数退出立即被销毁。

示例小程序,加深理解:

/* @filename: example-2.c */

#include <stdio.h>
int main(int argc, char *argv[])  {
    char arr[] = "hello world";     /* Stack段,rw--- */
    char *p = "hello world";        /* Text段,字符串直接量, r-x--  */
    arr[1] = 'l';
    *(++p) = 'l';   /* 出错了,Text段不能write */
    return 0;
}
变量p,它在Stack段,但它所指的”hello world”是一个字符串直接量,放在Text段。
/* @filename:example_2_2.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *get_str_1(){
    char str[] = "hello world";
    return str;
}

char *get_str_2() {
    char *str = "hello world";
    return str;
}
char *get_str_3(){
    char tmp[] = "hello world";
    char *str;
    str = (char *)malloc(12 * sizeof(char));
    memcpy(str, tmp, 12);
    return str;
}
int main(int argc, char *argv[])  {
    char *str_1 = get_str_1();  //出错了,Stack段中的数据在函数退出时就销毁了
    char *str_2 = get_str_2();  //正确,指向Text段中的字符直接量,退出程序后才会回收
    char *str_3 = get_str_3();  //正确,指向Heap段中的数据,还没free()
    printf("%s\n", str_1);
    printf("%s\n", str_2);
    printf("%s\n", str_3);
    if (str_3 != NULL)   {
        free(str_3);
        str_3 = NULL;
    }
    return 0;
}
函数get_str_1()返回Stack段数据,编译时会报错:

example_2_3.c: In function ‘get_str_1’:
example_2_3.c:7: warning: function returns address of local variable

Heap中的数据,如果不用了,应该尽早释放free()。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
char data_var  = '1';
char *mem_killer()
{
   char *p;
   p = (char *)malloc(1024*1024*4);
   memset(p, '\0', 1024*1024*4);
   p = &data_var;   //危险,内存泄露
   return p;
}
 
int main(int argc, char *argv[])
{
    char *p;
    for (;;)
    {
        p = mem_killer(); // 函数中malloc()分配的内存没办法free()
        printf("%c\n", *p);
        sleep(20);
    }
    return 0;
}

使用malloc(),特别要留意heap段中的内存不用时,尽早手工free()。通过top输出的VIRT和RES两值来观察进程占用VM和RAM大小。
因为Text, BSS, Data段在编译时已经决定了进程将占用多少VM。可以通过size,知道这些信息:

[root@localhost4 ~]# gcc example_2_3.c  -o example_2_3
[root@localhost4 ~]# size example_2_3
   text    data     bss     dec     hex filename
   1503     520      16    2039     7f7 example_2_3

2. 用户进程内存分配malloc

编码人员在编写程序之际,时常要处理变化数据,无法预料要处理的数据集变化是否大,所以除了变量之外,还需要动态分配内存。GNU libc库提供了二个内存分配函数,分别是malloc()和calloc()。调用malloc(size_t size)函数分配内存成功,总会分配size字节VM(再次强调不是RAM),并返回一个指向刚才所分配内存区域的开端地址。分配的内存会为进程一直保留着,直到你显示地调用free()释放它(当然,整个进程结束,静态和动态分配的内存都会被系统回收)。开发人员有责任尽早将动态分配的内存释放回系统。记住一句话:尽早free()!

我们来看看,malloc()小示例:

/* @filename:example_2_4.c */

#include <stdio.h>
#include <stdlib.h>
 
int main(int argc, char *argv[]){
    char *p_4kb, *p_128kb, *p_300kb;
    if ((p_4kb = malloc(4*1024)) != NULL){
        free(p_4kb);
</pre><pre name="code" class="cpp" style="font-size: 16px; line-height: 25.6px; font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif; color: rgb(62, 62, 62);">    }
    if ((p_128kb = malloc(128*1024)) != NULL) {
        free(p_128kb);
    }
    if ((p_300kb = malloc(300*1024)) != NULL)  {
        free(p_300kb);
    }
    return 0;
}

#gcc example_2_4.c –o example_2_4
#strace –t ./example_2_4

00:02:53 brk(0)                         = 0x8f58000
00:02:53 brk(0x8f7a000)                 = 0x8f7a000
00:02:53 brk(0x8f79000)                 = 0x8f79000
00:02:53 mmap2(NULL, 311296, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb772d000
00:02:53 munmap(0xb772d000, 311296)     = 0
系统调用brk(0)取得当前堆的地址,也称为断点。

通过跟踪系统内核调用,可见glibc函数malloc()总是通过brk()或mmap()系统调用来满足内存分配需求。函数malloc(),根据不同大小内存要求来选择brk(),还是mmap() 128Kbytes是临界值:

1) 小块内存(<=128kbytes),会调用brk(),它将数据段的最高地址往更高处推(堆从底部向上增长)。

2)大块内存,则使用mmap()进行匿名映射(设置标志MAP_ANONYMOUS)来分配内存,与堆无关,在堆之外。

这样做是有道理的,试想:如果大块内存,也调用brk(),则容易被小块内存钉住,必竟用大块内存不是很频繁;反过来,小块内存分配更为频繁得多,如果也使用mmap(),频繁的创建内存映射会导致更多的开销,还有一点就是,内存映射的大小要求必须是“页”(单位,内存页面大小,默认4Kbytes或8Kbytes)的倍数,如果只是为了”hello world”这样小数据就映射一“页”内存,那实在是太浪费了。

跟malloc()一样,释放内存函数free(),也会根据内存大小,选择使用brk()将断点往低处回推,或者选择调用munmap()解除映射。有一点需要注意:并不是每次调用free()小块内存,都会马上调用brk(),即堆并不会在每次内存被释放后就被缩减,而是会被glibc保留给下次malloc()使用(必竟小块内存分配较为频繁),直到glibc发现堆空闲大小显著大于内存分配所需数量时,则会调用brk()。但每次free()大块内存,都会调用munmap()解除映射.

3. 缺页处理

每次调用malloc(),系统都只是给进程分配线性地址(VM),并没有随即分配页框(RAM)。系统尽量将分配页框的工作推迟到最后一刻—用到时缺页异常处理。这种页框按需延迟分配策略最大好处之一:充分有效地善用系统稀缺资源RAM。

当指针引用的内存页没有驻留在RAM中,即在RAM找不到与之对应的页框,则会发生缺页异常(对进程来说是透明的),内核便陷入缺页异常处理。发生缺页异常有几种情况:

1.只分配了线性地址,并没有分配页框,常发生在第一次访问某内存页。

2.已经分配了页框,但页框被回收,换出至磁盘(交换区)。

3.引用的内存页,在进程空间之外,不属于该进程,可能已被free()。

我们使用一段伪代码来大致了解缺页异常。

/* @filename: example_2_5.c */
…
demo() {
    char *p;
    if ((p = malloc(1024*100)) != NULL)  // L0:分配了100Kbytes线性地址
    {
        *p = 't';      // L1
         … 
        *p = 'm';      // L2:过去了很长一段时间,不管系统忙否,长久不用的页框都有可能被回收
         p[4096] = 'p';// L3
          …
        free(p);       //L4
		if (p == NULL) {
			*p = 'l';  // L5
		}
    }
}
…
  • L0,函数malloc()通过brk()给进程分配了100Kbytes的线性地址区域(VM).然而,系统并没有随即分配页框(RAM)。即此时,进程没有占用100Kbytes的物理内存。这也表明了,你时常在使用top的时候VIRT值增大,而RES值却不变的原因。

  • L1,通过*p引用了100Kbytes的第一页(4Kbytes)。因为是第一次引用此页,在RAM中找不到与之相对应的页框。发生缺页异常(对于进程而言缺页异常是透明的),系统灵敏地捕获这一异常,进入缺页异常处理阶段:接下来,系统会分配一个页框(RAM)映射给它。我们把这种情况(被访问的页还没有被放在任何一个页框中,内核分配一新的页框并适当初始化来满足调用请求),也称为Demand Paging。

  • L2,过了很长一段时间,通过*p再次引用100Kbytes的第一页。若系统在RAM找不到它映射的页框(可能交换至磁盘了)。发生缺页异常,并被系统捕获进入缺页异常处理。接下来,系统则会分配一页页框(RAM),找到备份在磁盘的那“页”,并将它换入内存(其实因为换入操作比较昂贵,所以不总是只换入一页,而是预换入多页。这也表明某些文档说:”vmstat某时出现不少si并不能意味着物理内存不足”)。凡是类似这种会迫使进程去睡眠(很可能是由于当前磁盘数据填充至页框(RAM)所花的时间),阻塞当前进程的缺页异常处理称为主缺页(major falut),也称为大缺页(参见下图)。相反,不会阻塞进程的缺页,称为次缺页(minor fault),也称为小缺面。

  • L3,引用了100Kbytes的第二页。参见第一次访问100Kbytes第一页, Demand Paging。

  • L4,释放了内存:线性地址区域被删除,页框也被释放。

  • L5,再次通过*p引用内存页,已被free()了(用户进程本身并不知道)。发生缺页异常,缺面异常处理程序会检查出这个缺页不在进程内存空间之内。对待这种编程错误引起的缺页异常,系统会杀掉这个进程,并且报告著名的段错误(Segmentation fault)。

4. 页框回收SWAP

随着网络并发用户数量增多,进程数量越来越多(比如一般守护进程会fork()子进程来处理用户请求),缺页异常也就更频繁,需要缓存更多的磁盘数据(参考下篇OS Page Cache),RAM也就越来越紧少。为了保证有够用的页框供给缺页异常处理,Linux有一套自己的做法,称为PFRA。PFRA总会从用户态进内存程空间和页面缓存中,“窃取”页框满足供给。所谓”窃取”,指的是:将用户进程内存空间对应占用的页框中的数据swap out至磁盘(称为交换区),或者将OS页面缓存中的内存页(还有用户进程mmap()的内存页)flush(同步fsync())至磁盘设备。

swap out: 由于RAM资源不足,PFRA会将部分匿名页框的数据写入到交换区(swap area)(磁盘),备份之,这个动作称为so(swap out)。

swap in:等到发生内存缺页异常的时候,缺页异常处理程序会将交换区(磁盘)的页面又读回物理内存,这个动作称为si(swap in)。

每次Swapping,都有可能不只是一页数据,不管是si,还是so。Swapping意味着磁盘操作,更新页表等操作,这些操作开销都不小,会阻塞用户态进程。所以,持续飚高的si/so意味着物理内存资源是性能瓶颈。

如果你观察到因为RAM不足导致系统病态式般慢,通常都是因为缺页异常处理,以及PFRA在”盗页”。我们从以下几个方面了解PFRA。

候选页框:找出哪些页框是可以被回收?

  • 进程内存空间占用的页框,比如数据段中的页(Heap, Data),还有在Heap与Stack之间的匿名映射页(比如由malloc()分配的大内存)。但不包括Stack段中的页。

  • 进程空间mmap()的内存页,有映射文件,非匿名映射。

  • 缓存在页面缓存中Buffer/Cache占用的页框。也称OS Page Cache。

页框回收策略:确定了要回收的页框,就要进一步确定先回收哪些候选页框

  • 尽量先回收页面缓存中的Buffer/Cache。其次再回收内存空间占用的页框。

  • 进程空间占用的页框,要是没有被锁定,都可以回收。所以,当某进程睡眠久了,占用的页框会逐渐地交换出去至交换区。

  • 使收LRU置换算法,将那些久而未用的页框优先被回收。这种被放在LRU的unused链表的页,常被认为接下来也不太可能会被引用。

  • 相对回收Buffer/Cache而言,回收进程内存页,昂贵很多。所以,Linux默认只有swap_tendency(交换倾向值)值不小于100时,才会选择换出进程占用的RES。其实交换倾向值描述的是:系统越忙,且RES都被进程占用了,Buffer/Cache只占了一点点的时候,才开始回收进程占用页框。PS:这正表明了,某些DBA提议将MySQL InnoDB服务器vm.swappiness值设置为0,以此让InnoDB Buffer Pool数据在RES呆得更久。

  • 如果实在是没有页框可回收,PFRA使出最狠一招,杀掉一个用户态进程,并释放这些被占的页框。当然,这个被杀的进程不是胡乱选的,至少应该是占用较多页框,运行优选级低,且不是root用户的进程。

激活回收页框:什么时候会回收页框?

  • 紧急回收。系统内核发现没有够用的页框分配,供给读文件和内存缺页处理的时候,系统内核开始”紧急回收页框”。唤醒pdflush内核线程,先将1024页脏页从页面缓存写回磁盘。然后开始回收32页框,若反复回收13次,还收不齐32页框,则发狠杀一个进程。

  • 周期性回收。在紧急回收之前,PFRA还会唤醒内核线程kswapd。为了避免更多的“紧急回收”,当发现空闲页框数量低于设置的警告值时,内核线程kswapd就会被唤醒,回收页框。直到空闲的页框的数量达到设定的安全值。PS:当RES资源紧张的时候,你可以通过ps命令看到更多的kswapd线程被唤醒。

  • OOM。在高峰时期,RES高度紧张的时候,kswapd持续回收的页框供不应求,直到进入”紧急回收”,直到 OOM。

5. linux内存信息

我们通过free的输出内存内容:
#free
total used free shared buffers cached
Mem: 16402432 16360492 41940 0 465404 12714880
-/+ buffers/cache: 3180208 13222224
Swap: 8193108 264 8192844

total:物理内存的总大小。used:已经使用的物理内存多小。
free:空闲的物理内存值。
shared:多个进程共享的内存值。
buffers/cached:磁盘缓存的大小。
第三行(-/+ buffers/cached):代表磁盘缓存使用状态。
第四行:Swap表示交换空间内存使用状态。
公式:
-buffers/cache 的内存数:used - buffers - cached
+buffers/cache 的内存数:free + buffers + cached
可用的memory=free memory+buffers+cached
free命令输出的内存状态,可以通过两个角度来查看:一个是从内核的角度来看,一个是从应用层的角度来看的。


1.从内核的角度来查看内存的状态:(对于OS,buffers/cached 都是属于被使用)
        内核目前可以直接分配到,不需要额外的操作,即为上面free命令输出中第二行Mem项的值,可以看出,此系统物理内存有4G,空闲的内存只有41940K,也就是40M多一点,我们来做一个这样的计算:
       16402432-16360492=41940
       就是总的物理内存减去已经使用的物理内存得到的就是空闲的物理内存大小。
       注意:这里的可用内存值41940并不包含处于buffers和cached状态的内存大小。
      注意:实际上,内核完全控制着内存的使用情况,linux会在需要内存的时候,或在系统运行逐步推进时,将buffers和cached状态的内存变为free状态的内存,以供系统使用。

2.从应用层的角度来看系统内存的使用状态
      也就是linux上运行的应用程序可以使用的内存大小,即free命令第三行“(-/+ buffers/cached)”的输出,可以看到,此系统已经使用的内存才3180208K,而空闲的内存达到13222224K,继续做这样一个计算:
      41940(Men:free)+(465404(Men:buffers)+12714880(Men:cached))=13222224(-/+buffers/cached:free)
      通过这个等式可知,应用程序可用的物理内存值是Mem项的free值加上buffers和cached值之和,也就是说,这个free值是包括buffers和cached项大小的。
       对于应用程序来说,buffers/cached占有的内存是可用的,因为buffers/cached是为了提高文件读取的性能,当应用程序需要用到内存的时候buffers/cached会很快地被回收,以供应用程序使用


6. buffer和cache是什么

1)、计算机领域的buffer和cache

buffer和cache是两个在计算机技术中被用滥的名词,放在不通语境下会有不同的意义。

Cache:高速缓存,是位于CPU与主内存间的一种容量较小但速度很高的存储器。由于CPU的速度远高于主内存,CPU直接从内存中存取数据要等待一定时间周期,Cache中保存着CPU刚用过或循环使用的一部分数据,当CPU再次使用该部分数据时可从Cache中直接调用这样就减少了CPU的等待时间提高了系统的效率。Cache又分为一级Cache(L1 Cache)和二级Cache(L2 Cache),L1 Cache集成在CPU内部,L2 Cache早期一般是焊在主板上现在也都集成在CPU内部,常见的容量有256KB或512KB L2 Cache

Buffer:缓冲区,一个用于存储速度不同步的设备或优先级不同的设备之间传输数据的区域。通过缓冲区,可以使进程之间的相互等待变少,从而使从速度慢的设备读入数据时,速度快的设备的操作进程不发生间断。

cache最初用于cpu cache, 主要原因是cpu 与memory, 由于cpu快,memory跟不上,且有些值使用次数多,所以放入cache中,主要目的是,重复使用, 并且一级\二级物理cache速度快,
buffer主要用于disk与 memory,主要是保护硬盘或减少网络传输的次数(内存数据表现dataSet).当然也可以提高速度(不会立即写入硬盘或直接从硬盘中读出的数据马上显示),重复使用,最初最主要的目的是保护disk。

2)、linux Free的buffer和cache

linuxFree中的buffer和cache:(它们都是占用内存):

在Linux操作系统中,当应用程序需要读取文件中的数据时,操作系统先分配一些内存,将数据从磁盘读入到这些内存中,然后

再将数据分发给应用程序;当需要往文件中写数据时,操作系统先分配内存接收用户数据,然后再将数据从内存写到磁盘上。然而,如果有大量数据需要从磁盘读取到内存或者由内存写入磁盘时,系统的读写性能就变得非常低下,因为无论是从磁盘读数据,还是写数据到磁盘,都是一个很消耗时间和资源的过程,在这种情况下,为了提高磁盘存取效率, Linux做了一些精心的设计, 除了对dentry进行缓存(用于VFS,加速文件路径名到inode的转换), 还采取了两种主要Cache方式:Buffer Cache和Page Cache。前者针对磁盘块的读写,后者针对文件inode的读写。这些Cache有效缩短了 I/O系统调用(比如read,write,getdents)的时间。"

buffers与cached都是内存操作,用来保存系统曾经打开过的文件以及文件属性信息,这样当操作系统需要读取某些文件时,会首先在buffers与cached内存区查找,如果找到,直接读出传送给应用程序,如果没有找到需要数据,才从磁盘读取,这就是操作系统的缓存机制,通过缓存,大大提高了操作系统的性能。但buffers与cached缓冲的内容却是不同的。

buffers:作为buffer cache的内存,是用来缓冲块设备做的,对块的操作会使用buffer cache进行缓存,它只记录文件系统的元数据(metadata)以及 tracking in-flight pages。比如我们在格式化文件系统的时候,查找系统文件等。

cached:作为page cache的内存,是用来给文件做缓冲。如果 cache 的值很大,说明cache住的文件数很多。如果频繁访问到的文件都能被cache住,那么磁盘的读IO bi会非常小。

更通俗一点说:buffers主要用来存放目录里面有什么内容\文件的属性\权限等等。cached直接用来记忆我们打开过的文件和程序。

一般情况下两个缓存系统是一起配合使用的:

磁盘的操作有逻辑级(文件系统)和物理级(磁盘块),这两种Cache就是分别缓存逻辑和物理级数据的。
Page cache实际上是针对文件系统的,是文件的缓存,在文件层面上的数据会缓存到page cache。文件的逻辑层需要映射到实际的物理磁盘,这种映射关系由文件系统来完成。当page cache的数据需要刷新时,page cache中的数据交给buffer cache,因为Buffer Cache就是缓存磁盘块的。但是这种处理在2.6版本的内核之后就变的很简单了,没有真正意义上的cache操作。
Buffer cache是针对磁盘块的缓存,也就是在没有文件系统的情况下,直接对磁盘进行操作的数据会缓存到buffer cache中,例如,文件系统的元数据都会缓存到buffer cache中。
简单说来,page cache用来缓存文件数据,buffer cache用来缓存磁盘数据。在有文件系统的情况下,对文件操作,那么数据会缓存到page cache,如果直接采用dd等工具对磁盘进行读写,那么数据会缓存到buffer cache。

比如当我们对一个文件进行写操作的时候,page cache的内容会被改变,而buffer cache则可以用来将page标记为不同的缓冲区,并记录是哪一个缓冲区被修改了。

这样,内核在后续执行脏数据的回写(writeback)时,就不用将整个page写回,而只需要写回修改的部分即可。

为了验证我们的结论是否正确。

buffers验证

我们在执行find /* -name *.conf之前的free数值:

[root@localhost4 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:   3740        649       3090          0         74        123
-/+ buffers/cache:        450       3289
Swap:         3871          0       3871

find /* -name *.conf

看看buffers的值是否变化:

             total       used       free     shared    buffers     cached
Mem:          3740       1183       2556          0        486        123
-/+ buffers/cache:        573       3166
Swap:         3871          0       3871

buffers为486MB, cached不变 ,buffers变大啦

然后重复执行find命令,看看两次显示速度有何不同。

cached验证:

cached:可以通过vi打开一个非常大的文件,看看cached的变化,然后再次vi这个文件,感觉一下两次打开的速度有何异同,是不是第二次打开的速度明显快于第一次呢?此外我们通过复制文件: 


[root@localhost4 ~]#  cp -r /etc ~/test/
[root@localhost4 ~]# free -m   
             total       used       free     shared    buffers     cached
Mem:      3740       1261       2479          0        487        194
-/+ buffers/cache:        579       3160
Swap:         3871          0       3871

都cached已经变大啦。

Linux操作系统的内存运行原理,很大程度上是根据服务器的需求来设计的,例如系统的缓冲机制会把经常使用到的文件和数据缓存在cached中,linux总是在力求缓存更多的数据和信息,这样再次需要这些数据时可以直接从内存中取,而不需要有一个漫长的磁盘操作,这种设计思路提高了系统的整体性能。

2、如何回收cached

Linux内核会在内存将要耗尽的时候,触发内存回收的工作,以便释放出内存给急需内存的进程使用。
一般情况下,这个操作中主要的内存释放都来自于对buffer/cache的释放。尤其是被使用更多的cache空间。既然它主要用来做缓存,只是在内存够用的时候加快进程对文件的读写速度,那么在内存压力较大的情况下,当然有必要清空释放cache,作为free空间分给相关进程使用。
所以一般情况下,我们认为buffer/cache空间可以被释放,这个理解是正确的。
但是这种清缓存的工作也并不是没有成本。理解cache是干什么的就可以明白清缓存必须保证cache中的数据跟对应文件中的数据一致,才能对cache进行释放。
所以伴随着cache清除的行为的,一般都是系统IO飙高。因为内核要对比cache中的数据和对应硬盘文件上的数据是否一致,如果不一致需要写回,之后才能回收。
在系统中除了内存将被耗尽的时候可以清缓存以外,我们还可以使用下面这个文件来人工触发缓存清除的操作:

/proc是一个虚拟文件系统,我们可以通过对它的读写操作做为与kernel实体间进行通信的一种手段.也就是说可以通过修改/proc中的文件,来对当前kernel的行为做出调整.那么我们可以通过调整/proc/sys/vm/drop_caches来释放内存.操作如下:


[root@localhost4 ~]# cat /proc/sys/vm/drop_caches
0

首先,/proc/sys/vm/drop_caches的值,默认为0

这个文件可以设置的值分别为1、2、3。它们所表示的含义为:

echo 1 > /proc/sys/vm/drop_caches:表示清除page cache。

echo 2 > /proc/sys/vm/drop_caches:表示清除回收slab分配器中的对象(包括目录项缓存和inode缓存)。slab分配器是内核中管理内存的一种机制,其中很多缓存数据实现都是用的pagecache。

echo 3 > /proc/sys/vm/drop_caches:表示清除page cache和slab分配器中的缓存对象。


[root@localhost4 ~]# echo 3 > /proc/sys/vm/drop_caches
[root@localhost4 ~]# free -m
             total       used       free     shared    buffers     cached
Mem:          3740        465       3274          0          0         29
-/+ buffers/cache:        435       3304
Swap:         3871          0       3871

再来运行free命令,buffers为0MB,cached为29MB.那么有效的释放了buffer和cache.


7. Linux SWAP使用情况

如果系统的物理内存用光了,则会用到swap。系统就会跑得很慢,但仍能运行;如果Swap空间用光了,那么系统就会发生错误。通常会出现“application is out of memory”的错误,严重时会造成服务进程的死锁。所以要高度重视。如果常常swap用很多,可能你就要考虑加物理内存了.这也是linux看内存是否够用的标准。

我们通过free,top,cat /proc/swaps、swapon -s 等命令查看Swap分区的情况.

通过vmstat -n 1也能查看到当前系统使用内存的变化:


[root@localhost4 ~]# vmstat -n 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 3350920   3632  30724    0    0    20     8   27   51  0  0 99  0  0
 0  0      0 3350912   3632  30776    0    0     0     0   96  166  0  0 100  0  0

Memory(内存):
swpd: 使用虚拟内存大小
free: 可用内存大小
buff: 用作缓冲的内存大小
cache: 用作缓存的内存大小

Swap:
si (swap in ): 每秒从交换区写到内存的大小
so(swap out): 每秒写入交换区的内存大小

在Linux内核 2.6.16中引入了一个系统内存接口特性,这个接口位于/proc/$pid/目录下的smaps文件中 ,一看内容发现是进程内存映像信息,比同一目录下的maps文件更详细些。
cat /proc/1/smaps
ps -ef|grep nginx   
root      2164  2163  0 10:48 ?        00:00:00 nginx: master process 
www       2165  2164  0 10:48 ?        00:00:00 nginx: worker process

cat /proc/2164/smaps
 ...
7fffa8962000-7fffa8977000 rw-p 00000000 00:00 0                          [stack]
Size:                 88 kB
Rss:                  20 kB
Pss:                  12 kB
Shared_Clean:          0 kB
Shared_Dirty:          8 kB
Private_Clean:         0 kB
Private_Dirty:        12 kB
Referenced:           20 kB
Anonymous:            20 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB

解释下samps里面的内容:

7fffa8962000-7fffa8977000 是该虚拟内存段的开始和结束位置
rw-p 内存段的权限,rw是指可读写,p是指私有,如果是s则为共享
00000000该虚拟内存段在对应的映射文件中的偏移量
00:00 文件的主设备和次设备号
0被映射到虚拟内存的文件的索引节点号
[stack] 被映射到虚拟内存的文件名称
Size 是进程使用内存空间,并不一定实际分配了内存(VSS)
Rss是实际分配的内存(不需要缺页中断就可以使用的)
Shared_Clean 和其他进程共享的未改写页面
Shared_Dirty 和其他进程共享的已改写页面
Private_Clean 未改写的私有页面页面
Private_Dirty 已改写的私有页面页面
Swap 存在于交换分区的数据大小(如果物理内存有限,可能存在一部分在主存一部分在交换分区)
Pss是平摊计算后的使用内存(有些内存会和其他进程共享,例如mmap进来的)

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

初次体验hiphop-php

facebook在github上发布了hiphop-php的源代码。之前听说这玩意能把php代码翻译成c++代码,然后带来巨大的性能提升,所以第一时间编译了一份hiphop-php。 我的机器环境是...

samba-3源码安装一:编译安装

系统是redhat9 2.4.20-8,系统自带装了samba-2.2.7a-7.9.0下载samba最新版,文件名samba-3.0.24.tar.gz1、解压、编译、安装#tar xzvf sam...

Ubuntu下,dpkg安装出错的修复

参考 http://www.khattam.info/2009/08/04/solved-subprocess-pre-removal-script-returned-error-exit-stat...

linux内存使用分析

  • 2015年12月07日 10:24
  • 87KB
  • 下载

嵌入式LINUX内存使用与性能优化

  • 2013年05月23日 17:39
  • 29.91MB
  • 下载

Linux 下修改Tomcat使用的JVM内存大小

常见的内存溢出有以下两种: java.lang.OutOfMemoryError: PermGen space java.lang.OutOfMemoryError: Java heap space...
  • lipp555
  • lipp555
  • 2016年08月10日 14:42
  • 2461

linux 共享内存简单使用

  • 2008年08月24日 17:40
  • 2KB
  • 下载

嵌入式Linux内存使用与性能优化.pdf

  • 2012年07月02日 20:06
  • 1.53MB
  • 下载

linux 查看cpu, 内存当前使用情况命令

在系统维护的过程中,随时可能有需要查看 CPU 使用率,并根据相应信息分析系统状况的需要。在 CentOS 中,可以通过 top 命令来查看 CPU 使用状况。运行 top 命令后,CPU 使用状态会...
  • pyjfoot
  • pyjfoot
  • 2012年09月17日 20:02
  • 5225

Linux 内核对内存的使用方法.pdf

  • 2010年01月20日 23:00
  • 358KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:了解Linux 内存使用
举报原因:
原因补充:

(最多只允许输入30个字)