我们专注服务于当下互联网基础设施建设与云计算、大数据时代的各种需求!

oom_killer与内存管理策略

oom_killer 默认配置下,当没有内存可以用而又要用到内存时,Linux内核的oom_killer(out of memory killer)会扫描一遍占用内存最多的程序(可能有多个),并把它们结束掉。
这种扫描其实代价还是挺大的,可以选择让oom_killer不要扫描出用内存最多的进程,只是解决掉申请内存的那些进程: sysctl -w vm.oom_kill_allocating_task = 1 使用这样的设置,oom_killer不会去花时间寻找占内存最多的进程杀掉,大内存程序就有一定机会幸免。但是在内存 用完的那一瞬间,谁去申请或者使用一片空白的内存,谁就会悲剧,而且可能是几个进程一起被杀掉,充满了不可预测性(比如,Xorg被杀掉,于是许多程序连 带就挂掉了,再比如后台的mysqld被杀掉也会带来许多不方便),而且也没有避免pdflush在最后关头让硬盘灯狂闪的情况。 总之,oom_killer很不和谐,最好不要让它出场。
在这一点上,openSolaris似乎做的就比较好,从外表上看,在内存不够的时候,系统不会去主动杀掉正在运行的程序,而是拒绝运行新的程序,并且运行中的程序如果申请内存的话就会被暂停,直至有内存可以给它的时候才继续运行。 overcommit 那么系统为什么不能提前检测到内存用完呢,malloc是有可能返回NULL的啊?现在的操作系统中,允许过分地申请内存,如果只是申请内存而没有实际使用的话,可以申请到比实际内存大许多的空间(比如用malloc申请内存,while(1) malloc(x);这样的程序都可以运行好长时间),只有一旦开始用(比如用memset去填),才会计入真正的内存使用,这时候如果内存真的不够了,那么oom_killer就上场了。 目前的Linux提供了一些选项用来调整这种内存策略 :-)

默认情况下,vm.overcommit_memory = 0,这时候可以申请到比较多的内存,但是仍然会在一定的时候申请失败。

还有更宽松一些的,如果 vm.overcommit_memory = 1,所有的malloc都会无条件成功 相当可怕的世界。

最后一种选择就是这个了: sysctl -w vm.overcommit_memory = 2 这时候,对申请内存总数有严格的限制,malloc会在超过限制的时候返回NULL,应用程序可以适当处理这种情况,而oom_killer再也不会蹦出来了,pdflush也不会让硬盘转得系统没响应,如果一个程序不能适当处理这种情况,就立即挂掉,干净利落。

但是这也有坏处,这时候参数vm.overcommit_ratio也会起作用,默认是50,意思是只能分配到实际物理内存的50%。如果没有交换区的话,overcommit_ratio设置得小就会很悲剧,几乎什么都做不了。 那把它设置成100,事情就非常和谐了?没有这样简单,这里的限制是申请内存总数的限制,如果申请了却没有实际用到的话,也是计入总数的。这样的话,实际内存没有用完,程序也很有可能申请不到内存,有一些内存就被浪费了。 虽然overcommit_ratio可以被设置成大于100的数,但是到底设置成多少确是个棘手的问题,设置大了,就和没有限制一样,内存用完时硬盘会狂转,系统会失去响应一段时间,oom_killer有可能会上场,设置小了,有可能几百兆的内存被白白浪费了 检查内存信息可以看到:

% cat /proc/meminfo

MemTotal: 2064616 kB

MemFree: 1556672 kB

….

CommitLimit: 2064616 kB

Committed_AS: 769068 kB

….

其中Committed_AS是程序申请的内存总和,不能超过CommitLimit。

很明显地看到Committed_AS+MemFree比MemTotal大,看起来把CommitLimit设置成Committed_AS+MemFree比较合适。