Linux内存工作原理之内存分配与回收
此文章部分内容由(小红书 www.xiaohongshutuiguang.cn)转载提供
a>内存分配
malloc()是C标准库提供的内存分配函数,对应到系统调用上,有两种实现方式,即brk()和mmap()。
对小块内存(小于128K),C标准库使用brk()来分配,也就是通过移动堆顶的位置来分配内存。这些内存释放后并不会立即归还系统,而是被缓存起来,这样就可以重复使用。
brk()方式的缓存,可以减少缺页异常的发生,提高内存访问效率。不过由于这些内存没有归还系统,在内存工作繁忙是,频繁的内存分配和释放会造成内存碎片
对大块内存(大于128K),则直接使用内存映射mmap()来分配,也就是在文件映射段找一块空闲内存分配出去。
mmap()方式分配的内存,会在释放时直接归还系统,所以每次mmap都会发生缺页异常。在内存工作繁忙时,频繁的内存分配会造成大量的缺页异常,使内核的管理负担增大。这也是malloc只对大块内存使用mmap的原因
当这两种调用发生后,其实并没有真正分配内存。这些内存都只在首次访问时才分配,也就是通过缺页异常进入内核中,再由内核来分配内存。Linux使用伙伴系统来管理内存分配,与MMU的页管理一样,伙伴系统也是以页为单位来管理内存的,并通过相邻页的合并,较少内存碎片化(比如brk方式造成的内存碎片)。
b>内存回收
对于内存来说,如果只分配而不释放,就会造成内存泄露,甚至会耗尽系统内存。所以,在应用程序用完内存后,需要调用 free() 或 unmap()
,来释放不用的内存。在发现内存紧张时,系统就会通过一系列机制来回收内存:
回收缓存,比如使用 LRU(Least Recently Used)算法,回收最近最少使用的内存页面
回收不常访问的内存,把不常用的内存通过交换分区直接写到磁盘中
杀死进程,内存紧张时系统会通过 OOM(Out Of Memory),直接杀掉占用大量内存的进程
其中,第二种方式回收不常访问的内存时,会用到交换分区(Swap)。Swap其实就是吧一块磁盘空间当成内存来用。它可以吧进程暂时不用的数据存储到磁盘中(这个过程称为换出),当进程访问这些内存时,在从磁盘读取这些数据到内存(这个过程称为换入)。所以Swap把系统的可用内存变大了。不过通常只在内存不足时,才会发生Swap交换,并且由于磁盘读写的速度远比内存慢,Swap会导致严重的内存性能问题。
第三种方式提到的OOM,其实时内核的一种保护机制。它监控进程的内存使用情况,并且使用 oom_score 为每个进程的内存使用情况进行评分:
一个进程消耗的内存越大,oom_score就越大
一个进程运行占用的CPU越多,oom_score就越小
进程的oom_score越大,代表消耗的内存越多,也就越容易被OOM杀死,从而可以更好保护系统。结合实际需求,可以通过 /proc
文件系统,手动设置进程的
oom_adj,从而调整oom_score。oom_adj的范围是[-17,15],数值越大表示进程越容易被OOM杀死;数值越小表示进程越不容易被OOM杀死,其中
-17 表示禁止OOM。例如:手动调整sshd进程的oom_adj 为 -16,保障sshd进程不容易被OOM杀死