Memroy 的 Over Commit 与 OOM
概述
over commit memory 机制与 out of memory 机制
over-commit memory 机制
Linux 内核根据应用程序的要求分配内存,通常来说应用程序分配了内存但是并没有实际全部使用,为了提高性能,这部分没用的内存可以留作它用,这部分内存是属于每个进程的,内核直接回收利用的话比较麻烦,所以内核采用一种过度分配内存(over-commit memory)的办法来间接利用这部分 “空闲” 的内存,提高整体内存的使用效率。一般来说这样做没有问题,但当大多数应用程序都消耗完自己的内存的时候麻烦就来了,因为这些应用程序的内存需求加起来超出了物理内存(包括 swap)的容量,内核(OOM killer)必须杀掉一些进程才能腾出空间保障系统正常运行。用银行的例子来讲可能更容易懂一些,部分人取钱的时候银行不怕,银行有足够的存款应付,当全国人民(或者绝大多数)都取钱而且每个人都想把自己钱取完的时候银行的麻烦就来了,银行实际上是没有这么多钱给大家取的。
out of memory 机制(OOM)
某时刻应用程序大量请求内存导致系统内存不足造成的,这通常会触发 Linux 内核里的 Out of Memory (OOM) killer,OOM killer 会杀掉某个进程以腾出内存留给系统用,不致于让系统立刻崩溃
内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码 linux/mm/oom_kill.c,当系统内存不足的时候,out_of_memory() 被触发,然后调用 select_bad_process() 选择一个 “bad” 进程杀掉,如何判断和选择一个 “bad” 进程呢,总不能随机选吧?挑选的过程由 oom_badness() 决定,挑选的算法和想法都很简单很朴实:最 bad 的那个进程就是那个最占用内存的进程。
OOM 触发后的 Message 信息
Nov 24 19:52:22 dr-2 kernel: dsm_sa_datamgrd invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0
Nov 24 19:52:22 dr-2 kernel: dsm_sa_datamgrd cpuset=/ mems_allowed=0-1
Nov 24 19:52:22 dr-2 kernel: Pid: 4917, comm: dsm_sa_datamgrd Not tainted 2.6.32-279.19.1.el6.x86_64 #1
Nov 24 19:52:22 dr-2 kernel: Call Trace:
Nov 24 19:52:22 dr-2 kernel: [<ffffffff810c29e1>] ? cpuset_print_task_mems_allowed+0x91/0xb0
Nov 24 19:52:22 dr-2 kernel: [<ffffffff81112d40>] ? dump_header+0x90/0x1b0
Nov 24 19:52:22 dr-2 kernel: [<ffffffff810e064e>] ? __delayacct_freepages_end+0x2e/0x30
Nov 24 19:52:22 dr-2 kernel: [<ffffffff8120dfec>] ? security_real_capable_noaudit+0x3c/0x70
Nov 24 19:52:22 dr-2 kernel: [<ffffffff811131c2>] ? oom_kill_process+0x82/0x2a0
Nov 24 19:52:22 dr-2 kernel: [<ffffffff811130be>] ? select_bad_process+0x9e/0x120
Nov 24 19:52:22 dr-2 kernel: [<ffffffff81113600>] ? out_of_memory+0x220/0x3c0
Nov 24 19:52:22 dr-2 kernel: [<ffffffff8112331e>] ? __alloc_pages_nodemask+0x89e/0x940
Nov 24 19:52:22 dr-2 kernel: [<ffffffff811574ea>] ? alloc_pages_current+0xaa/0x110
Nov 24 19:52:22 dr-2 kernel: [<ffffffff811101c7>] ? __page_cache_alloc+0x87/0x90
Nov 24 19:52:22 dr-2 kernel: [<ffffffff81125cfb>] ? __do_page_cache_readahead+0xdb/0x210
Nov 24 19:52:22 dr-2 kernel: [<ffffffff81125e51>] ? ra_submit+0x21/0x30
Nov 24 19:52:22 dr-2 kernel: [<ffffffff811114f3>] ? filemap_fault+0x4c3/0x500
Nov 24 19:52:22 dr-2 kernel: [<ffffffff8113a754>] ? __do_fault+0x54/0x510
Nov 24 19:52:22 dr-2 kernel: [<ffffffff8113ad07>] ? handle_pte_fault+0xf7/0xb50
Nov 24 19:52:22 dr-2 kernel: [<ffffffff8105a5c3>] ? perf_event_task_sched_out+0x33/0x80
Nov 24 19:52:22 dr-2 kernel: [<ffffffff8113b99a>] ? handle_mm_fault+0x23a/0x310
Nov 24 19:52:22 dr-2 kernel: [<ffffffff810432d9>] ? __do_page_fault+0x139/0x480
Nov 24 19:52:22 dr-2 kernel: [<ffffffff81095bdf>] ? hrtimer_try_to_cancel+0x3f/0xd0
Nov 24 19:52:22 dr-2 kernel: [<ffffffff81095c92>] ? hrtimer_cancel+0x22/0x30
Nov 24 19:52:22 dr-2 kernel: [<ffffffff814eb723>] ? do_nanosleep+0x93/0xc0
Nov 24 19:52:22 dr-2 kernel: [<ffffffff81095d64>] ? hrtimer_nanosleep+0xc4/0x180
Nov 24 19:52:22 dr-2 kernel: [<ffffffff81094af0>] ? hrtimer_wakeup+0x0/0x30
Nov 24 19:52:22 dr-2 kernel: [<ffffffff814ef68e>] ? do_page_fault+0x3e/0xa0
Nov 24 19:52:22 dr-2 kernel: [<ffffffff814eca45>] ? page_fault+0x25/0x30
Nov 24 19:52:22 dr-2 kernel: Mem-Info:
Nov 24 19:52:22 dr-2 kernel: Node 0 Normal per-cpu:
Nov 24 19:52:22 dr-2 kernel: CPU 0: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 1: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 2: hi: 186, btch: 31 usd: 2
Nov 24 19:52:22 dr-2 kernel: CPU 3: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 4: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 5: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 6: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 7: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 8: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 9: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 10: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 11: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 12: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 13: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 14: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 15: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: Node 1 DMA per-cpu:
Nov 24 19:52:22 dr-2 kernel: CPU 0: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 1: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 2: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 3: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 4: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 5: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 6: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 7: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 8: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 9: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 10: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 11: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 12: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 13: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 14: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 15: hi: 0, btch: 1 usd: 0
Nov 24 19:52:22 dr-2 kernel: Node 1 DMA32 per-cpu:
Nov 24 19:52:22 dr-2 kernel: CPU 0: hi: 186, btch: 31 usd: 69
Nov 24 19:52:22 dr-2 kernel: CPU 1: hi: 186, btch: 31 usd: 31
Nov 24 19:52:22 dr-2 kernel: CPU 2: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 3: hi: 186, btch: 31 usd: 46
Nov 24 19:52:22 dr-2 kernel: CPU 4: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 5: hi: 186, btch: 31 usd: 99
Nov 24 19:52:22 dr-2 kernel: CPU 6: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 7: hi: 186, btch: 31 usd: 55
Nov 24 19:52:22 dr-2 kernel: CPU 8: hi: 186, btch: 31 usd: 42
Nov 24 19:52:22 dr-2 kernel: CPU 9: hi: 186, btch: 31 usd: 20
Nov 24 19:52:22 dr-2 kernel: CPU 10: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 11: hi: 186, btch: 31 usd: 30
Nov 24 19:52:22 dr-2 kernel: CPU 12: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 13: hi: 186, btch: 31 usd: 168
Nov 24 19:52:22 dr-2 kernel: CPU 14: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 15: hi: 186, btch: 31 usd: 30
Nov 24 19:52:22 dr-2 kernel: Node 1 Normal per-cpu:
Nov 24 19:52:22 dr-2 kernel: CPU 0: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 1: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 2: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 3: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 4: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 5: hi: 186, btch: 31 usd: 2
Nov 24 19:52:22 dr-2 kernel: CPU 6: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 7: hi: 186, btch: 31 usd: 2
Nov 24 19:52:22 dr-2 kernel: CPU 8: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 9: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 10: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 11: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 12: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 13: hi: 186, btch: 31 usd: 2
Nov 24 19:52:22 dr-2 kernel: CPU 14: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: CPU 15: hi: 186, btch: 31 usd: 0
Nov 24 19:52:22 dr-2 kernel: active_anon:3693 inactive_anon:959 isolated_anon:0
Nov 24 19:52:22 dr-2 kernel: active_file:17 inactive_file:1177 isolated_file:0
Nov 24 19:52:22 dr-2 kernel: unevictable:0 dirty:6 writeback:0 unstable:0
Nov 24 19:52:22 dr-2 kernel: free:19262 slab_reclaimable:94836 slab_unreclaimable:3898229
Nov 24 19:52:22 dr-2 kernel: mapped:230 shmem:0 pagetables:5388 bounce:0
Nov 24 19:52:22 dr-2 kernel: Node 0 Normal free:15468kB min:45120kB low:56400kB high:67680kB active_anon:0kB inactive_anon:136kB active_file:12kB inactive_file:516kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:8273920kB mlocked:0kB dirty:4kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:349292kB slab_unreclaimable:7792080kB kernel_stack:2824kB pagetables:10744kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:1098 all_unreclaimable? yes
Nov 24 19:52:22 dr-2 kernel: lowmem_reserve[]: 0 0 0 0
Nov 24 19:52:22 dr-2 kernel: Node 1 DMA free:15740kB min:80kB low:100kB high:120kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15352kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
Nov 24 19:52:22 dr-2 kernel: lowmem_reserve[]: 0 3243 8041 8041
Nov 24 19:52:22 dr-2 kernel: Node 1 DMA32 free:36536kB min:18112kB low:22640kB high:27168kB active_anon:14772kB inactive_anon:3644kB active_file:56kB inactive_file:4008kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:3321540kB mlocked:0kB dirty:16kB writeback:0kB mapped:916kB shmem:0kB slab_reclaimable:840kB slab_unreclaimable:2982720kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:27584 all_unreclaimable? yes
Nov 24 19:52:22 dr-2 kernel: lowmem_reserve[]: 0 0 4797 4797
Nov 24 19:52:22 dr-2 kernel: Node 1 Normal free:9304kB min:26788kB low:33484kB high:40180kB active_anon:0kB inactive_anon:56kB active_file:0kB inactive_file:184kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:4912640kB mlocked:0kB dirty:4kB writeback:0kB mapped:4kB shmem:0kB slab_reclaimable:29212kB slab_unreclaimable:4818116kB kernel_stack:824kB pagetables:10808kB unstable:0kB bounce:0kB writeback_tmp:0kB pages_scanned:380 all_unreclaimable? yes
Nov 24 19:52:22 dr-2 kernel: lowmem_reserve[]: 0 0 0 0
Nov 24 19:52:22 dr-2 kernel: Node 0 Normal: 2528*4kB 407*8kB 4*16kB 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 1*2048kB 0*4096kB = 15480kB
Nov 24 19:52:22 dr-2 kernel: Node 1 DMA: 1*4kB 1*8kB 1*16kB 1*32kB 1*64kB 0*128kB 1*256kB 0*512kB 1*1024kB 1*2048kB 3*4096kB = 15740kB
Nov 24 19:52:22 dr-2 kernel: Node 1 DMA32: 1*4kB 537*8kB 320*16kB 307*32kB 135*64kB 45*128kB 0*256kB 0*512kB 0*1024kB 1*2048kB 0*4096kB = 35692kB
Nov 24 19:52:22 dr-2 kernel: Node 1 Normal: 1670*4kB 8*8kB 0*16kB 4*32kB 8*64kB 7*128kB 2*256kB 1*512kB 0*1024kB 0*2048kB 0*4096kB = 9304kB
Nov 24 19:52:22 dr-2 kernel: 2609 total pagecache pages
Nov 24 19:52:22 dr-2 kernel: 1323 pages in swap cache
Nov 24 19:52:22 dr-2 kernel: Swap cache stats: add 1919969, delete 1918646, find 72763/93573
Nov 24 19:52:22 dr-2 kernel: Free swap = 25878484kB
Nov 24 19:52:22 dr-2 kernel: Total swap = 32767992kB
Nov 24 19:52:22 dr-2 kernel: 4194303 pages RAM
Nov 24 19:52:22 dr-2 kernel: 115262 pages reserved
Nov 24 19:52:22 dr-2 kernel: 1110 pages shared
Nov 24 19:52:22 dr-2 kernel: 3295246 pages non-shared
Nov 24 19:52:22 dr-2 kernel: [ pid ] uid tgid total_vm rss cpu oom_adj oom_score_adj name
Nov 24 19:52:22 dr-2 kernel: [ 624] 0 624 2763 0 1 -17 -1000 udevd
Nov 24 19:52:22 dr-2 kernel: [ 1981] 0 1981 23294 2 0 -17 -1000 auditd
Nov 24 19:52:22 dr-2 kernel: [ 1997] 0 1997 82373 196 0 0 0 rsyslogd
Nov 24 19:52:22 dr-2 kernel: [ 2027] 0 2027 2287 94 0 0 0 irqbalance
Nov 24 19:52:22 dr-2 kernel: [ 2054] 81 2054 5377 1 1 0 0 dbus-daemon
Nov 24 19:52:22 dr-2 kernel: [ 2078] 0 2078 1020 0 9 0 0 acpid
Nov 24 19:52:22 dr-2 kernel: [ 2087] 68 2087 6338 1 7 0 0 hald
Nov 24 19:52:22 dr-2 kernel: [ 2088] 0 2088 4527 1 0 0 0 hald-runner
Nov 24 19:52:22 dr-2 kernel: [ 2116] 0 2116 5063 1 2 0 0 hald-addon-inpu
Nov 24 19:52:22 dr-2 kernel: [ 2131] 68 2131 4452 1 1 0 0 hald-addon-acpi
Nov 24 19:52:22 dr-2 kernel: [ 2153] 0 2153 16019 0 1 -17 -1000 sshd
Nov 24 19:52:22 dr-2 kernel: [ 2169] 0 2169 29303 1 7 0 0 crond
Nov 24 19:52:22 dr-2 kernel: [ 2180] 0 2180 5364 0 0 0 0 atd
Nov 24 19:52:22 dr-2 kernel: [ 2473] 0 2473 272563 264 1 0 0 dsm_sa_datamgrd
Nov 24 19:52:22 dr-2 kernel: [ 2586] 0 2586 122414 0 7 0 0 dsm_sa_datamgrd
Nov 24 19:52:22 dr-2 kernel: [ 2601] 0 2601 73220 30 1 0 0 dsm_sa_eventmgr
Nov 24 19:52:22 dr-2 kernel: [ 2660] 0 2660 125789 89 12 0 0 dsm_sa_snmpd
Nov 24 19:52:22 dr-2 kernel: [ 2734] 0 2734 159845 1 1 0 0 dsm_om_shrsvcd
Nov 24 19:52:22 dr-2 kernel: [ 2767] 0 2767 1016 1 8 0 0 mingetty
Nov 24 19:52:22 dr-2 kernel: [ 2769] 0 2769 1016 1 4 0 0 mingetty
Nov 24 19:52:22 dr-2 kernel: [ 2771] 0 2771 1016 1 7 0 0 mingetty
Nov 24 19:52:22 dr-2 kernel: [ 2773] 0 2773 1016 1 12 0 0 mingetty
Nov 24 19:52:22 dr-2 kernel: [ 2779] 0 2779 1016 1 10 0 0 mingetty
Nov 24 19:52:22 dr-2 kernel: [ 5065] 0 5065 1028466 1 3 0 0 console-kit-dae
Nov 24 19:52:22 dr-2 kernel: [11520] 0 11520 1016 1 0 0 0 mingetty
Nov 24 19:52:22 dr-2 kernel: [23919] 38 23919 6485 1 7 0 0 ntpd
Nov 24 19:52:22 dr-2 kernel: [14167] 0 14167 27401 34 15 0 0 keepalived
Nov 24 19:52:22 dr-2 kernel: [14168] 0 14168 27961 88 15 0 0 keepalived
Nov 24 19:52:22 dr-2 kernel: [14169] 0 14169 27927 31 15 0 0 keepalived
Nov 24 19:52:22 dr-2 kernel: [ 2448] 0 2448 2762 0 2 -17 -1000 udevd
Nov 24 19:52:22 dr-2 kernel: [ 2546] 0 2546 2762 0 3 -17 -1000 udevd
Nov 24 19:52:22 dr-2 kernel: [20557] 188 20557 2140125 3183 0 0 0 haproxy
Nov 24 19:52:22 dr-2 kernel: [16066] 0 16066 95625 285 7 0 0 snmpd
Nov 24 19:52:22 dr-2 kernel: Out of memory: Kill process 20557 (haproxy) score 133 or sacrifice child
Nov 24 19:52:22 dr-2 kernel: Killed process 20557, UID 188, (haproxy) total-vm:8560500kB, anon-rss:12468kB, file-rss:264kB
Nov 24 19:52:40 dr-2 kernel: __ratelimit: 8552 callbacks suppressed
message 信息中的名词解释
进程所占资源列表相关信息
- pid # 进程 ID 号
- uid # 该进程用户的 UserID
- tgid #
- total_vm # 该进程所占用的虚拟内存页,1page=4k 内存,所以实际占用需要用该值乘以 4
- rss # 该进程所占用的实际内存页,1page=4k 内存,所以实际占用需要用该值乘以 4
- cpu #
- oom_adj # oom 计算出的该进程的。详见本文《oom killer 怎么挑选进程》章节
- oom_score_adj # oom 给该进程的分数。详见本文《oom killer 怎么挑选进程》章节
- name # 进程名
OOM Killer
是 Linux 内核设计的一种机制,在内存不足的会后,选择一个占用内存较大的进程并 kill 掉这个进程,以满足内存申请的需求(内存不足的时候该怎么办,其实是个两难的事情,oom killer 算是提供了一种方案吧)
在什么时候触发?
前面说了,在内存不足的时候触发,主要牵涉到【linux 的物理内存结构】和【overcommit 机制】
2.1 内存结构 node、zone、page、order
对于物理内存内存,linux 会把它分很多区(zone),zone 上还有 node 的概念,zone 下面是很多 page,这些 page 是根据 buddy 分配算法组织的,看下面两张图:
上面的概念做下简单的介绍,对后面分析 oom killer 日志很有必要:
Node:每个 CPU 下的本地内存节点就是一个 Node,如果是 UMA 架构下,就只有一个 Node0,在 NUMA 架构下,会有多个 Node
Zone:每个 Node 会划分很多域 Zone,大概有下面这些:
ZONE_DMA:定义适合 DMA 的内存域,该区域的长度依赖于处理器类型。比如 ARM 所有地址都可以进行 DMA,所以该值可以很大,或者干脆不定义 DMA 类型的内存域。而在 IA-32 的处理器上,一般定义为 16M。
ZONE_DMA32:只在 64 位系统上有效,为一些 32 位外设 DMA 时分配内存。如果物理内存大于 4G,该值为 4G,否则与实际的物理内存大小相同。
ZONE_NORMAL:定义可直接映射到内核空间的普通内存域。在 64 位系统上,如果物理内存小于 4G,该内存域为空。而在 32 位系统上,该值最大为 896M。
ZONE_HIGHMEM:只在 32 位系统上有效,标记超过 896M 范围的内存。在 64 位系统上,由于地址空间巨大,超过 4G 的内存都分布在 ZONE_NORMA 内存域。
ZONE_MOVABLE:伪内存域,为了实现减小内存碎片的机制。
分配价值链
除了只能在某个区域分配的内存(比如 ZONE_DMA),普通的内存分配会有一个“价值”的层次结构,按分配的“廉价度”依次为:ZONE_HIGHMEM > ZONE_NORMAL > ZONE_DMA。
即内核在进行内存分配时,优先从高端内存进行分配,其次是普通内存,最后才是 DMA 内存
Page:zone 下面就是真正的内存页了,每个页基础大小是 4K,他们维护在一个叫 free_area 的数组结构中
order:数组的 index,也叫 order,实际对应的是 page 的大小,比如 order 为 0,那么就是一堆 1 个空闲页(4K)组成的链表,order 为 1,就是一堆 2 个空闲页(8K)组成的链表,order 为 2,就是一堆 4 个空闲页(16K)组成的链表
2.2 overcommit
根据 2.1,已经知道物理内存的大概结构以及分配的规则,不过实际上还有虚拟内存的存在,他的 overcommit 机制和 oom killer 有很大关系:
在实际申请内存的时候,比如申请 1G,并不会在物理区域中分配 1G 的真实物理内存,而是分配 1G 的虚拟内存,等到需要的时候才去真正申请物理内存,也就是说申请不等于分配
所以说,可以申请比物理内存实际大的内存,也就是 overcommit,这样会面临一个问题,就是当真的需要这么多内存的时候怎么办—>oom killer!
vm.overcommit_memory 接受三种值:
- 0 – Heuristic overcommit handling. 这是缺省值,它允许 overcommit,但过于明目张胆的 overcommit 会被拒绝,比如 malloc 一次性申请的内存大小就超过了系统总内存
- 1 – Always overcommit. 允许 overcommit,对内存申请来者不拒。
- 2 – Don’t overcommit. 禁止 overcommit。
oom killer 怎么挑选进程?
linux 会为每个进程算一个分数,最终他会将分数最高的进程 kill
/proc/oom_score_adj
- 在计算最终的 badness score 时,会在计算结果是中加上 oom_score_adj,取值范围为-1000 到 1000
- 如果将该值设置为-1000,则进程永远不会被杀死,因为此时 badness score 永远返回 0。
/proc/oom_adj
- 取值是-17 到+15,取值越高,越容易被干掉。如果是-17,则表示不能被 kill
- 该设置参数的存在是为了和旧版本的内核兼容
/proc/oom_score
- 这个值是系统综合进程的内存消耗量、CPU 时间(utime + stime)、存活时间(uptime - start time)和 oom_adj 计算出的,消耗内存越多分越高,存活时间越长分越低
子进程内存:Linux 在计算进程的内存消耗的时候,会将子进程所耗内存的一半同时算到父进程中。这样,那些子进程比较多的进程就要小心了。
其他参数(不是很关键,不解释了)
- /proc/sys/vm/oom_dump_tasks
- /proc/sys/vm/oom_kill_allocating_task
- /proc/sys/vm/panic_on_oom
关闭 OOM killer
sysctl -w vm.overcommit_memory=2
echo “vm.overcommit_memory=2” » /etc/sysctl.conf
3.1 找出最有可能被杀掉的进程
cat > oomscore.sh <<EOF
#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do
printf "%2d %5d %s\n" \
"$(cat $proc/oom_score)" \
"$(basename $proc)" \
"$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)"
done 2>/dev/null | sort -nr | head -n 10
EOF
chmod +x oomscore.sh
./oomscore.sh
18 981 /usr/sbin/mysqld
4 31359 -bash
4 31056 -bash
1 31358 sshd: root@pts/6
1 31244 sshd: vpsee [priv]
1 31159 -bash
1 31158 sudo -i
1 31055 sshd: root@pts/3
1 30912 sshd: vpsee [priv]
1 29547 /usr/sbin/sshd -D
3.2 避免的 oom killer 的方案
- 直接修改/proc/PID/oom_adj 文件,将其置位-17
- 修改/proc/sys/vm/lowmem_reserve_ratio
- 直接关闭 oom-killer
参考:
- node & zone
- 理解 LINUX 的 MEMORY OVERCOMMIT
- linux OOM-killer 机制(杀掉进程,释放内存)
- Taming the OOM killer
- linux OOM 机制分析
- 理解和配置 Linux 下的 OOM Killer
- ubuntu 解决 cache 逐渐变大导致 oom-killer 将某些进程杀死的情况
这篇是一例 oom killer 日志的具体分析,有疑问的可以先看上一篇:
下面是一台 8G 内存上的一次 oom killer 的日志,上面跑的是 RocketMQ 3.2.6,java 堆配置:-server -Xms4g -Xmx4g -Xmn2g -XX:PermSize=128m -XX:MaxPermSize=320m
Jun 4 17:19:10 iZ23tpcto8eZ kernel: AliYunDun invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0
Jun 4 17:19:10 iZ23tpcto8eZ kernel: AliYunDun cpuset=/ mems_allowed=0
Jun 4 17:19:10 iZ23tpcto8eZ kernel: active_anon:1813257 inactive_anon:37301 isolated_anon:0 active_file:84 inactive_file:0 isolated_file:0 unevictable:0 dirty:0 writeback:0 unstable:0 free:23900 slab_reclaimable:34218 slab_unreclaimable:5636 mapped:1252 shmem:100531 pagetables:68092 bounce:0 free_cma:0
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 DMA free:15900kB min:132kB low:164kB high:196kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15992kB managed:15908kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:8kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
Jun 4 17:19:10 iZ23tpcto8eZ kernel: lowmem_reserve[]: 0 2801 7792 7792
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 DMA32 free:43500kB min:24252kB low:30312kB high:36376kB
active_anon:2643608kB(2.5G) inactive_anon:61560kB active_file:40kB inactive_file:40kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:3129216kB managed:2869240kB mlocked:0kB dirty:0kB writeback:0kB mapped:748kB shmem:160024kB slab_reclaimable:54996kB slab_unreclaimable:6816kB kernel_stack:704kB pagetables:67440kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:275 all_unreclaimable? yes
Jun 4 17:19:10 iZ23tpcto8eZ kernel: lowmem_reserve[]: 0 0 4990 4990
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 Normal free:36200kB min:43192kB low:53988kB high:64788kB
active_anon:4609420kB(4.3G) inactive_anon:87644kB active_file:296kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:5242880kB managed:5110124kB mlocked:0kB dirty:0kB writeback:0kB mapped:4260kB shmem:242100kB slab_reclaimable:81876kB slab_unreclaimable:15720kB kernel_stack:1808kB pagetables:204928kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:511 all_unreclaimable? yes
Jun 4 17:19:10 iZ23tpcto8eZ kernel: lowmem_reserve[]: 0 0 0 0
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 DMA: 1*4kB (U) 1*8kB (U) 1*16kB (U) 0*32kB 2*64kB (U) 1*128kB (U) 1*256kB (U) 0*512kB 1*1024kB (U) 1*2048kB (R) 3*4096kB (M) = 15900kB
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 DMA32: 1281*4kB (UEM) 825*8kB (UEM) 1404*16kB (UEM) 290*32kB (EM) 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 43468kB
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 Normal: 1441*4kB (UEM) 3177*8kB (UEM) 315*16kB (UEM) 0*32kB 0*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 0*2048kB 0*4096kB = 36220kB
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB
Jun 4 17:19:10 iZ23tpcto8eZ kernel: 100592 total pagecache pages
Jun 4 17:19:10 iZ23tpcto8eZ kernel: 0 pages in swap cache
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Swap cache stats: add 0, delete 0, find 0/0
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Free swap = 0kB
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Total swap = 0kB
Jun 4 17:19:10 iZ23tpcto8eZ kernel: 2097151 pages RAM
Jun 4 17:19:10 iZ23tpcto8eZ kernel: 94167 pages reserved
Jun 4 17:19:10 iZ23tpcto8eZ kernel: 284736 pages shared
Jun 4 17:19:10 iZ23tpcto8eZ kernel: 1976069 pages non-shared
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 338] 0 338 10748 844 25 0 0 systemd-journal
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 351] 0 351 26113 61 20 0 0 lvmetad
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 368] 0 368 10509 149 23 0 -1000 systemd-udevd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 521] 0 521 170342 908 178 0 0 rsyslogd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 525] 0 525 8671 82 21 0 0 systemd-logind
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 526] 81 526 7157 96 19 0 -900 dbus-daemon
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 530] 0 530 31575 162 17 0 0 crond
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 540] 28 540 160978 131 37 0 0 nscd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 548] 0 548 27501 30 10 0 0 agetty
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 588] 0 588 1621 26 9 0 0 iprinit
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 590] 0 590 1621 25 9 0 0 iprupdate
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 601] 0 601 9781 23 8 0 0 iprdump
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 838] 38 838 7399 169 18 0 0 ntpd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 881] 0 881 386 44 4 0 0 aliyun-service
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 5973] 1000 5973 41595 165 32 0 0 gmond
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 3829] 0 3829 33413 292 67 0 0 sshd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 3831] 1000 3831 33582 476 68 0 0 sshd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 3832] 1000 3832 29407 622 16 0 0 bash
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [14638] 0 14638 20697 210 42 0 -1000 sshd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [11531] 0 11531 33413 293 66 0 0 sshd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [11533] 1000 11533 33413 292 64 0 0 sshd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [11534] 1000 11534 29361 584 15 0 0 bash
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 3172] 0 3172 6338 161 17 0 0 AliYunDunUpdate
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 3224] 0 3224 32867 2270 61 0 0 AliYunDun
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 5417] 1000 5417 28279 51 14 0 0 sh
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 5421] 1000 5421 28279 53 13 0 0 sh
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [ 5424] 1000 5424 36913689 1537770 66407 0 0 java
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [17132] 0 17132 21804 215 44 0 0 zabbix_agentd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [17133] 0 17133 21804 285 43 0 0 zabbix_agentd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [17134] 0 17134 21866 290 44 0 0 zabbix_agentd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [17135] 0 17135 21866 290 44 0 0 zabbix_agentd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [17136] 0 17136 21841 290 44 0 0 zabbix_agentd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [17137] 0 17137 21804 245 43 0 0 zabbix_agentd
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [13669] 1000 13669 28279 51 14 0 0 sh
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [13673] 1000 13673 28279 50 13 0 0 sh
Jun 4 17:19:10 iZ23tpcto8eZ kernel: [13675] 1000 13675 879675 204324 494 0 0 java
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Out of memory: Kill process 5424 (java) score 800 or sacrifice child
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Killed process 5424 (java) total-vm:147654756kB, anon-rss:6151080kB, file-rss:0kB
各列字段解释:
min 下的内存是保留给内核使用的;当到达 min,会触发内存的 direct reclaim
low 水位比 min 高一些,当内存可用量小于 low 的时候,会触发 kswapd 回收内存,当 kswapd 慢慢的将内存 回收到 high 水位,就开始继续睡眠
1.谁申请内存以及谁被 kill 了? 这两个问题,可以从头部和尾部的日志分析出来:
Jun 4 17:19:10 iZ23tpcto8eZ kernel: AliYunDun invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0 Jun 4 17:19:10 iZ23tpcto8eZ kernel: Killed process 5424 (java) total-vm:147654756kB, anon-rss:6151080kB, file-rss:0kB
AliYunDun 申请内存,kill 掉了 java 进程 5424,他占用的内存是 6151080K(5.8G)
还有一个小问题可能会有疑问,那就是进程 5424 的 RSS(1537770)明明小于 6151080,实际是因为这里的 RSS 是 4K 位单位的,所以要乘以 4,算出来就对了
物理内存申请我们在上一篇分析了,会到不同的 Node 不同的 zone,那么这次申请的是哪一部分?这个可以从 gfp_mask=0x201da, order=0 分析出来,gfp_mask(get free page)是申请内存的时候,会传的一个标记位,里面包含三个信息:区域修饰符、行为修饰符、类型修饰符:
0X201da = 0x20000 | 0x100| 0x80 | 0x40 | 0x10 | 0x08 | 0x02 也就是下面几个值:___GFP_HARDWAL | ___GFP_COLD | ___GFP_FS | ___GFP_IO | ___GFP_MOVABLE| ___GFP_HIGHMEM
同时设置了___GFP_MOVABLE 和___GFP_HIGHMEM 会扫描 ZONE_MOVABLE,其实也就是会在 ZONE_NORMAL,再贴一次神图
另外 order 表示了本次申请内存的大小 0,也就是 4KB
也就是说 AliYunDun 尝试从 ZONE_NORMAL 申请 4KB 的内存,但是失败了,导致了 OOM KILLER
2.各个 zone 的情况如何?
接下来,自然就会问,连 4KB 都没有,那到底还有多少?看这部分日志:
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 DMA free:15900kB min:132kB low:164kB high:196kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15992kB managed:15908kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:8kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
Jun 4 17:19:10 iZ23tpcto8eZ kernel: lowmem_reserve[]: 0 2801 7792 7792
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 DMA32 free:43500kB min:24252kB low:30312kB high:36376kB
active_anon:2643608kB(2.5G) inactive_anon:61560kB active_file:40kB inactive_file:40kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:3129216kB managed:2869240kB mlocked:0kB dirty:0kB writeback:0kB mapped:748kB shmem:160024kB slab_reclaimable:54996kB slab_unreclaimable:6816kB kernel_stack:704kB pagetables:67440kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:275 all_unreclaimable? yes
Jun 4 17:19:10 iZ23tpcto8eZ kernel: lowmem_reserve[]: 0 0 4990 4990
Jun 4 17:19:10 iZ23tpcto8eZ kernel: Node 0 Normal free:36200kB min:43192kB low:53988kB high:64788kB
active_anon:4609420kB(4.3G) inactive_anon:87644kB active_file:296kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:5242880kB managed:5110124kB mlocked:0kB dirty:0kB writeback:0kB mapped:4260kB shmem:242100kB slab_reclaimable:81876kB slab_unreclaimable:15720kB kernel_stack:1808kB pagetables:204928kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:511 all_unreclaimable? yes
Jun 4 17:19:10 iZ23tpcto8eZ kernel: lowmem_reserve[]: 0 0 0 0
可以看到 Normal 还有 36200KB,DMA32 还有 43500KB,DMA 还有 15900KB,其中 Normal 的 free 确实小于 min,但是 DMA32 和 DMA 的 free 没问题啊?从上篇文章分析来看,分配是有链条的,Normal 不够了,会从 DMA32 以及 DMA 去请求分配,所以为什么分配失败了呢?
2.1 lowmem_reserve
虽然说分配内存会按照 Normal、DMA32、DMA 的顺序去分配,但是低端内存相对来说更宝贵些,为了防止低端内存被高端内存用完,linux 设计了保护机制,也就是 lowmen_reserve,从上面的日志看,他们的值是这样的:
- DMA(index=0): lowmem_reserve[]:0 2801 7792 7792
- DMA32(index=1): lowmem_reserve[]: 0 0 4990 4990
- Normal(index=2): lowmem_reserve[]: 0 0 0 0
lowmen_reserve 的值是一个数组,当 Normal(index=2)像 DMA32 申请内存的时候,需要满足条件:DMA32 high+lowmem_reserve[2] < free,才能申请,来算下:
Normal:从自己这里申请,free(36200) < min(43192),所以申请失败了(watermark[min]以下的内存属于系统的自留内存,用以满足特殊使用,所以不会给用户态的普通申请来用)
Normal 转到 DMA32 申请:
high(36376KB) + lowmem_reserve[2](4990)\*4=56336KB > DMA32 Free(43500KB)
,不允许申请Normal 转到 DMA 申请:
high(196KB) + lowmem_reserve[2](7792)\*4 = 31364KB > DMA Free(15900KB)
,不允许申请,所以….最终失败了2.2 min_free_kbytes
这里属于扩展知识了,和分析 oom 问题不大
我们知道了每个区都有 min、low、high,那他们是怎么计算出来的,就是根据 min_free_kbytes 计算出来的,他本身在系统初始化的时候计算,最小 128K,最大 64M
- watermark[min] = min_free_kbytes 换算为 page 单位即可,假设为 min_free_pages。(因为是每个 zone 各有一套 watermark 参数,实际计算效果是根据各个 zone 大小所占内存总大小的比例,而算出来的 per zone min_free_pages)
- watermark[low] = watermark[min] * 5 / 4
- watermark[high] = watermark[min] * 3 / 2
min 和 low 的区别:
min 下的内存是保留给内核使用的;当到达 min,会触发内存的 direct reclaim
low 水位比 min 高一些,当内存可用量小于 low 的时候,会触发 kswapd 回收内存,当 kswapd 慢慢的将内存 回收到 high 水位,就开始继续睡眠
3.最后的问题:java 为什么占用了这么多内存?
内存不足申请失败的细节都分析清楚了,剩下的问题就是为什么 java 会申请这么多内存(5.8G),明明-Xmx 配置的是 4G,加上 PermSize,也就最多 4.3G。
因为这上面跑的是 RocketMQ,他会有文件映射 mmap,所以在仔细分析 oom 日志之前,怀疑是 pagecache 占用,导致 RSS 为 5.8G,这带来了另一个问题,为什么 pagecache 没有回收?分析了日志以后,发现和 pagecache 基本没关系,看这个日志(换行是我后来加上的):
Jun 4 17:19:10 iZ23tpcto8eZ kernel: active_anon:1813257 inactive_anon:37301 isolated_anon:0 active_file:84
inactive_file:0 isolated_file:0
unevictable:0 dirty:0 writeback:0
unstable:0 free:23900
slab_reclaimable:34218
slab_unreclaimable:5636 mapped:1252
shmem:100531 pagetables:68092 bounce:0
free_cma:0
当时的内存大部分都是活跃的匿名页(active_anon 18132574KB=6.9G),其他的非活跃匿名页(inactive_anon 145M),活跃文件页(active_file 844=336KB),非活跃文件页(inactive_file 0),也就是说当时基本没有 pagecache,因为 pagecache 会属于文件页
并且,这台机器上的 gc log 没配置好,进程重启以后 gc 文件被覆盖了,另外被 oom killer 也没有 java dump,所以…..真的不知道到底为什么 java 占了 5.8G!!! 悬案还是没有解开 T_T
如果上层申请内存的速度太快,导致空闲内存降至 watermark[min]后,内核就会进行 direct reclaim(直接回收),即直接在应用程序的进程上下文中进行回收,再用回收上来的空闲页满足内存申请,因此实际会阻塞应用程序,带来一定的响应延迟
反馈
此页是否对你有帮助?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.