Memory Overcommit的意思是操作系统承诺给进程的内存大小超过了实际可用的内存.
一个保守的操作系统不会允许memory overcommit, 有多少就分配多少, 再申请就没有了, 这其实有些浪费内存, 因为进程实际使用到的内存往往比申请的内存要少
比如某个进程malloc()了200MB内存, 但实际上只用到了100MB, 按照UNIX/Linux的算法, 物理内存页的分配发生在使用的瞬间, 而不是在申请的瞬间, 也就是说未用到的100MB内存根本就没有分配, 这100MB内存就闲置了
下面这个概念很重要, 是理解memory overcommit的关键: *commit(或overcommit)针对的是内存申请, 内存申请不等于内存分配, 内存只在实际用到的时候才分配. *
Linux是允许memory overcommit的, 只要你来申请内存我就给你, 寄希望于进程实际上用不到那么多内存, 但万一用到那么多了呢?那就会发生类似“银行挤兑”的危机, 现金(内存)不足了.
Linux设计了一个OOM killer机制(OOM = out-of-memory)来处理这种危机: 挑选一个进程出来杀死, 以腾出部分内存, 如果还不够就继续杀…也可通过设置内核参数 vm.panic_on_oom 使得发生OOM时自动重启系统.
这都是有风险的机制, 重启有可能造成业务中断, 杀死进程也有可能导致业务中断, Linux 2.6之后允许通过内核参数 vm.overcommit_memory 禁止memory overcommit.
内核参数 vm.overcommit_memory 接受三种取值:
- 0 – Heuristic overcommit handling. 这是缺省值, 它允许overcommit, 但过于明目张胆的overcommit会被拒绝, 比如malloc一次性申请的内存大小就超过了系统总内存. Heuristic的意思是“试探式的”, 内核利用某种算法(对该算法的详细解释请看文末)猜测你的内存申请是否合理, 它认为不合理就会拒绝overcommit.
- 1 – Always overcommit. 允许overcommit, 对内存申请来者不拒.
- 2 – Don’t overcommit. 禁止overcommit.
关于禁止overcommit (vm.overcommit_memory=2) , 需要知道的是, 怎样才算是overcommit呢?kernel设有一个阈值, 申请的内存总数超过这个阈值就算overcommit, 在/proc/meminfo中可以看到这个阈值的大小:
1 | # grep -i commit /proc/meminfo |
CommitLimit 就是overcommit的阈值, 申请的内存总数超过CommitLimit的话就算是overcommit.
这个阈值是如何计算出来的呢?它既不是物理内存的大小, 也不是free memory的大小, 它是通过内核参数vm.overcommit_ratio或vm.overcommit_kbytes间接设置的, 公式如下:
1 |
|
$ sar -r
05:00:01 PM kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty
05:10:01 PM 160576 3648460 95.78 0 1846212 4939368 62.74 1390292 1854880 4
1 |
|
Heuristic overcommit算法在以下函数中实现, 基本上可以这么理解:
单次申请的内存大小不能超过 【free memory + free swap + pagecache的大小 + SLAB中可回收的部分】, 否则本次申请就会失败.
1 | 源文件: source/mm/mmap.c 以RHEL内核2.6.32-642为例 |
oom_badness v2.6.39版本
缺页中断
do_page_fault -> out_of_memory -> select_bad_process -> oom_badness。
在oom_badness即会对进程计算分数,分数越高被kill可能性越高。
该版本考虑维度如下:
rss、pagetable、swap使用量、是否root进程
1 | /** |
关于mmap只读共享的测试
测试代码在github.com/zsummer/zbase/tests/mapping_test 中
- 文件系统的内存映射分page cache 和buffer cache;
- 只读共享就是单纯的page cache;
- page cache 也会用到内存产生实际的内存使用(本来就是这样).
- 文件系统的page cache带来的实际内存使用也会计算进该进程的’commit’中 如果有多个进程 每个进程都会计算到commit中. 用于oom打分
- 操作系统中的commit则只有一个
从OOM角度来讲不区分共享内存 只考虑RSS(一直有有小的迭代 比如加入swap量, root进程判定等);
page cache的回收由kswapd周期检查 通过内存水位判定, 基础参数是min_free_kbytes
当前水线位置和详细信息在cat /proc/zoneinfo , 内存不够用时也会直接触发 .
因此 mmap文件的virt 地址空间和OOM没关系, mmap带来的rss会在被OOM killer之前回收掉不会被kill.