zoukankan      html  css  js  c++  java
  • 内存oom 可用内存和可申请内存

    转自 http://www.361way.com/linux-oom/6229.html

    一、OOM killer算法

    OOM是linux out of memory的简称,由于现网要做此类问题的优化,这里再总结下。OOM的算法策略具体见下图:

    二、OOM机制

    Linux用户内存都是读写时分配,所以系统发现需要内存基本上都是发生在handle_mm_fault()的时候(其他特殊流程类似,这里忽略),handle_mm_fault()要为缺的页分配内存,就会调alloc_pages()系列函数,从而调prepare_alloc_pages(),进而进入__alloc_pages_direct_reclaim(),这里已经把可以清到磁盘上的缓冲都清了一次了。这样之后还是分配不到内存,就只好进入OMM Killer了(pagefault_out_of_memory())。

    上面这段话是从知乎上看到的别人的分析,有兴趣的可以去源码。OOM本身说白了是为保证系统自身的正常运行而作的一个优化处理手段,但他也有一些问题,下面通俗的看下:

    系统中的内存只有可能被正在运行的进程和内核占据了,大家都不让,系统就只有死。内核是官家,进程是商家,官家不能杀,只好杀商家,商家杀一个也是杀,杀十个也是杀,那就杀个最胖的,少拉点仇恨。也就只能这样了,换你,你能怎么选?

    这样做的缺点:一个程序的行为有可能使另一个正常程序"躺枪“(一个进程A狂分配内存,导致系统把毫无关系的进程B给kill了)。

    三、OOM调优

    1、能否禁用OOM机制

    Red Hat Enteprise Linux 5、6和7无法完全禁用OOM-KILLER。可以通过overcommit_memory参数设置分配上限。

    1. overcommit_memory=0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
    2. overcommit_memory=1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
    3. overcommit_memory=2, 表示内核允许分配超过所有物理内存和交换空间总和的内存

    为什么会考虑调整overcommit_memory?

    一个保守的操作系统不会允许memory overcommit,有多少就分配多少,再申请就没有了,这其实有些浪费内存,因为进程实际使用到的内存往往比申请的内存要少,比如某个进程malloc()了200MB内存,但实际上只用到了100MB,按照UNIX/Linux的算法,物理内存页的分配发生在使用的瞬间,而不是在申请的瞬间,也就是说未用到的100MB内存根本就没有分配,这100MB内存就闲置了。

    默认情况下系统是保守的,值为overcommit_memory=0。该项我觉得设成1是可以的,但是不建议使用参数值2,参数设置为允许所有的内存分配(含SWAP)----- 即使主机可能已经无法响应了。因为这样可能会造成机器完全无响应,官家和商家一起同归于尽。

    后面会说他其他调整内存允许上限的方法,具体可以见下面部分。

    2、尽少重要进程被OOM-killer的机率

    为避免上面提到的A有问题把B杀了的情况,可以通过设置优先级,优先保证核以业务进程B不被杀掉。可以通过调整oom_killer得分来确定哪些进程被杀死。在/ proc / PID /中,有两个标记为oom_adj和oom_score的工具。 oom_adj的有效分数在-16到+15之间。该值用于使用一种算法来计算流程的“不良”程度,该算法还考虑了流程运行了多长时间以及其他因素。要查看当前的oom_killer得分,请查看该过程的oom_score。 oom_killer将首先杀死得分最高的进程。

    示例调整PID为12465的进程的oom_score,以使oom_killer杀死它的可能性降低。

    1. # cat /proc/12465/oom_score
    2. 79872
    3. # echo ‐5 > /proc/12465/oom_adj
    4. # cat /proc/12465/oom_score
    5. 78

    还有一个特殊值-17,它会为该进程禁用oom_killer。在下面的示例中,oom_score返回值0,表示该进程不会被杀死。

    1. # cat /proc/12465/oom_score
    2. 78
    3. # echo ‐17 > /proc/12465/oom_adj
    4. # cat /proc/12465/oom_score
    5. 0

    所以可以把重要进程的值设置为-17 。

    四、swap增加的必要性

    1、CommitLimit与Committed_AS

    先看下下面两个值:

    1. # grep -i commit /proc/meminfo
    2. CommitLimit: 6026080 kB
    3. Committed_AS: 7710484 kB

    CommitLimit 就是overcommit的阈值,申请的内存总数超过CommitLimit的话就算是overcommit。

    Committed_AS 表示所有进程已经申请的内存总大小,(注意是已经申请的,不是已经分配的),如果 Committed_AS 超过 CommitLimit 就表示发生了overcommit,超出越多表示 overcommit 越严重。Committed_AS 的含义换一种说法就是,如果要绝对保证不发生OOM (out of memory) 需要多少物理内存。

    这里有两个问题:虽然审请了,系统给不给是一会事(overcommit_memory);另外用不用得完,比如一个JAVA_OPTS中设置了Xmx为8G,并不表示最大申请这么多,java就一定实时达到了这个最大值。所以Committed_AS大于CommitLimit也不一定都是不正常,但尽量不要大于后者。

    2、CommitLimit是如何算出来的?

    CommitLimit 它既不是物理内存的大小,也不是free memory的大小,它是通过内核参数vm.overcommit_ratio或vm.overcommit_kbytes间接设置的,公式如下:

    1. CommitLimit = (Physical RAM * vm.overcommit_ratio / 100) + Swap

    vm.overcommit_ratio 是内核参数,缺省值是50,表示物理内存的50%。如果你不想使用比率,也可以直接指定内存的字节数大小,通过另一个内核参数 vm.overcommit_kbytes即可;如果使用了huge pages,那么需要从物理内存中减去,公式变成:

    1. CommitLimit = ([total RAM] - [total huge TLB RAM]) * vm.overcommit_ratio / 100 + swap

    所以由上面的公式可以推出,在没有swap的情况下,进程耗用的内存又比较多的时候,很容易造成Committed_AS达到CommitLimit的上限,甚至超过这个上限。那如减小系统的危机感呢?方法如下:

    • 增大overcommit_ratio值,由默认50,可以提高为100(或者修改vm.overcommit_kbytes值);
    • 增加swap(在没有swap的主机上)
    • 修改vm.overcommit_memory 的值为2

    显然在没有swap的主机,增加swap的方案相对最优。增加后,可以提高CommitLimit,同时又可以在主机确实内存不足时,临时有磁盘IO充当内存分配给进程临时用。

    五、增加swap前后%commit的变化

    1. %commit = Committed_AS/(MemTotal+SwapTotal)

    意思是_内存申请_占_物理内存与交换区之和_的百分比。以下是在增加swap前后,主机上该值的变化(该值在一定程序上反映了主机的危机感强弱)

    未增加swap之前:

     增加swap之后:

    而实际swap占用的内存只有几十M,如果担心swap的分配为影响IO情能,可以再修改vm.swappiness 参数的大小。

    参考页面:

    https://access.redhat.com/mt/zh-hans/solutions/20985

    http://linuxperf.com/?p=102

    https://blog.csdn.net/run_for_belief/article/details/83446344

  • 相关阅读:
    SpringBoot 项目集成增强版 SwaggerKnife4j 附常见问题及解决方案
    ZooKeeper 06 ZooKeeper 的常用命令
    ZooKeeper 04 ZooKeeper 集群的节点为什么必须是奇数个
    TCP扫描增强器实现65000端口,10S完成,快准狠(Go语言编程)
    集群服务器的网络连接状态接入ELK(可视化操作)
    golang的ping检测主机存活
    Gin编写邮件告警接口(添加配置,项目拆分)
    Gin编写邮件接口(支持多人发送)
    Linux操作系统账号密码失效检测
    Rsyslog同步集群服务器的网络连接状态
  • 原文地址:https://www.cnblogs.com/ligang0357/p/14048489.html
Copyright © 2011-2022 走看看