Out of memory: Kill process 问题

1.Overcommit策略 和 OOM killer
Linux中malloc返回非空指针,并不一定意味着指向的内存就是可用的,Linux下允许程序申请比系统可用内存更多的内存,这个特性叫Overcommit。
这样做是出于优化系统考虑,因为不是所有的程序申请了内存就立刻使用的,当你使用的时候说不定系统已经回收了一些资源了。
Linux下有3种Overcommit的策略(参考内核文档:vm/overcommit-accounting),可以在/proc/sys/vm/overcommit_memory配置。取0,1和2三个值,默认是0。
0:启发式策略,比较严重的Overcommit将不能得逞,比如你突然申请了128TB的内存。而轻微的Overcommit将被允许。另外,root能Overcommit的值比普通用户要稍微多些。
1:永远允许Overcommit,这种策略适合那些不能承受内存分配失败的应用,比如某些科学计算应用。
2:永远禁止Overcommit,在这个情况下,系统所能分配的内存不会超过swap+RAM*系数(/proc/sys/vm/overcmmit_ratio,默认50%,你可以调整),如果这么多资源已经用光,那么后面任何尝试申请内存的行为都会返回错误,这通常意味着此时没法运行任何新程序。
Overcommit导致的问题,如果申请的内存多余系统的RAM+swap,当真正使用的内存也超过RAM+swap时,内存不足怎么处理?
linux会启动OOM killer,挑选进程杀死,释放内存,来解决内存不足的问题;
OOM killer
OOM(out-of-memory) killer是通过/proc//oom_score这个值来决定哪个进程被干掉的。
这个值是系统综合进程的内存消耗量、CPU时间(utime + stime)、存活时间(uptime - start time)和oom_adj计算出的,消耗内存越多分越高,存活时间越长分越低。
总之,总的策略是:损失最少的工作,释放最大的内存同时不伤及无辜的用了很大内存的进程,并且杀掉的进程数尽量少。
参考:Linux下OOM Killer机制详解

2.top 命令中的VIRT、RES、SHR含义
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
16079 root 20 0 7204m 669m 110m S 0.0 17.5 0:22.17 rcsm-metic-exe
16069 root 20 0 2652m 511m 628 S 0.0 13.4 0:07.61 rcsm-metic-exe
16087 root 20 0 7259m 480m 724 S 0.0 12.6 164:15.85 rcsm-metic-exe
16082 root 20 0 7259m 480m 728 S 0.0 12.6 165:09.92 rcsm-metic-exe
16089 root 20 0 7259m 480m 672 S 0.0 12.5 163:28.89 rcsm-metic-exe
第一个值(VIRT)就是total_vm,即进程虚存的总大小,这个比较清晰,只要进程申请了内存,无论是malloc还是堆栈还是全局,都会计入这个值;
第二个值(RES)是file_rss+anon_rss,
第三个值(SHR)是file_rss。
RES要和SHR结合者看,内核把物理内存(不计算swap out的内存,RSS resident set size)分为了两部分,一部分是映射至文件的file_rss,一部分是没有映射至文件的即匿名内存anon_rss,完全和共不共享没有关系!
但file_rss为什么叫做shared呢?应该是一种指示性表述,表示这部分内存可能是共享的。但并不代表真正共享了。那么到底哪些计入file_rss?通过查阅相关代码,发现(可能有遗漏):
l 程序的代码段。
l 动态库的代码段。
l 通过mmap做的文件映射。
l 通过mmap做的匿名映射,但指明了MAP_SHARED属性。
l 通过shmget申请的共享内存。
即进程通过以上方式占用的物理内存,计入file_rss,也就是top的SHR字段。我们看到一般这些内存都是以共享方式存在。但如果某个动态库只一个进程在使用,它的代码段就没有被共享着。
反过来再来看anon_rss统计的内容,是否就一定是独占的?也不是,比如新fork之后的子进程,由于copy on write机制,在页面被修改之前,和父进程共享。这部分值并不体现在top命令的SHR字段内。
参考:剖析top命令显示的VIRT RES SHR值
另外,如果mmap做的匿名映射后,进程没有使用过这块内存(如果是fork出的子进程,没使用过表示fork后没使用过),只会计入total_vm, 不会计入file_rss,
可以使用如下代码测试,用top -p pid 查看:

    #include
    #include
    #include
    #include
    #define MM_SIZE 200*1024*1024
    int main()
    {
        pid_t pid = 0;  
        int*  sh_area = mmap(0, MM_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); 
        if(sh_area == MAP_FAILED){
            printf("mmap failed \n");
            return -1; 
        }   
        pid = fork(); 
        if(pid < 0){ 
            printf("fork error\n");
            return -1; 
        }   
        if(pid ==0){//child
           //memset(sh_area, 0, MM_SIZE); 
        }else{//parent
           //memset(sh_area, 0, MM_SIZE); 
        }   
        sleep(60);
        printf("exit pid:%d\n", pid);
        return 0;
    }

3.Out of memory: Kill process问题
1)查看/var/log/messages中的日志
从下面日志中看到,触发OOM后,18872被kill了,只使用了558+34MB的内存
Sep 19 14:58:00 test08 kernel: [ pid ] uid tgid total_vm rss cpu oom_adj oom_score_adj name
Sep 19 14:58:00 test08 kernel: [18871] 0 18871 502855 5227 0 0 0 mc-exe
Sep 19 14:58:00 test08 kernel: [18872] 0 18872 1842475 148013 1 0 0 mc-exe
Sep 19 14:58:00 test08 kernel: [18873] 0 18873 502855 3176 1 0 0 mc-exe
Sep 19 14:58:00 test08 kernel: Out of memory: Kill process 18872 (mc) score 43 or sacrifice child
Sep 19 14:58:00 test08 kernel: Killed process 18872, UID 0, (mc) total-vm:7369900kB, anon-rss:558044kB, file-rss:34008kB
其中,上面列表中total_vm,rss的单位是4K(单个内存页面的大小),18872进程使用了不到600M(anon-rss+file-rss)的内存,
根据每个进程的oom_score选出来了18872,kill掉了,18872进程并没有占用大量内存,也没有其他进程占用超过600M的物理内存,只是整个系统的物理内存(4G)不够用导致了,占用最大的进程被杀死了;
OOM触发的详细分析如下:
Sep 18 09:19:15 test08 kernel: mc invoked oom-killer: gfp_mask=0x280da, order=0, oom_adj=0, oom_score_adj=0
从上面日志看,gfp_mask=0x280da的低2位bit是2,表示此次申请内存是从Normal空间内存块申请内存,
Sep 18 09:19:15 test08 kernel: Node 0 Normal free:8356kB min:8464kB low:10580kB high:12696kB
上面的free表示Normal空间的空闲内存,min、low、high是三个阈值, 当free小于min时,会触发OOM
下面这行日志显示了Normal空间空闲的内存块的详细信息,也可以通过命令cat /proc/buddyinfo查看当前系统得空闲块
Sep 18 09:19:15 test08 kernel: Node 0 Normal: 109*4kB 62*8kB 56*16kB 26*32kB 7*64kB 11*128kB 9*256kB 3*512kB 0*1024kB 0*2048kB 0*4096kB = 8356kB
参考:analyzing OOM killer logs
Out of memory, OOM killer输出信息分析
触发OOM的原因是物理内存不足,可能swap还有大量空闲
2)关于触发OOM时swap还有空闲的问题
从下面的日志看,swap的还有约3G的空闲,只是物理内存耗尽了(低于min阈值)
Sep 19 14:58:00 test08 kernel: Free swap = 3000364kB
Sep 19 14:58:00 test08 kernel: Total swap = 4194300kB
Sep 19 14:58:00 test08 kernel: 1048575 pages RAM
Sep 19 14:58:00 test08 kernel: 67932 pages reserved
Sep 19 14:58:00 test08 kernel: 275848 pages shared
Sep 19 14:58:00 test08 kernel: 907335 pages non-shared
swap还有空闲时,物理内存(RAM)没有使用swap out,就触发了OOM
这个和系统的/proc/sys/vm/swappiness有关,swappiness设置的范围是0~100,越大表示越倾向于使用swap, 此环境上/proc/sys/vm/swappiness=0
参考:VM.SWAPPINESS=0在最新内核中可能会OOM,MYSQL被意外KILL
RAM不足时,应该swap out还是触发OOM的讨论Is it possible to trigger OOM-killer on forced swapping?
增大/proc/sys/vm/swappiness的值,可以减少RAM的占用量,可能会避免进程被kill掉,但是会因为使用swap比较多,导致系统变慢

4.查看运行进程使用swap大小
方法一:cat /proc/8204/status
在VmSwap:行可以看到进程使用的swap大小
[root@dev08 ~]# cat /proc/8204/status | grep VmSwap
VmSwap: 10220 kB
方法二:top -m -p pid
在USED-RES即是进程使用的swap空间大小
PID USER PR NI USED RES SHR S %CPU %MEM TIME+ COMMAND
8204 root 20 0 25124 14m 14m S 0.0 0.4 0:03.60 rcsm-metic-exe
参考:查看进程swap使用量及相关分析
Linux服务端swap配置和监控

你可能感兴趣的:(操作系统及应用软件)