Linux内存碎片化的应对措施

Linux系统长时间运行会出现内存碎片化的问题,本文主要提供了应对该问题的一些措施。

问题现象

实例内部署的业务偶然出现响应时间过长或者系统调用时间过长,系统的sys指标也会相应的增高,伙伴系统会缺少高阶内存(order大于3的内存)。例如,运行cat /proc/buddyinfo命令的返回结果如下所示,其中从第4列开始,每一列对应伙伴系统的不同order空闲内存。

cat /proc/buddyinfo 
Node 0, zone      DMA      1      0      0   1   2    1    1     0     1      1     3 
Node 0, zone    DMA32   3173    856    529   0   0    0    0     0     0      0     0 
Node 0, zone   Normal  19030   8688   7823   0   0    0    0     0     0      0     0

可能原因

Linux系统在长时间运行后,连续的大块物理内存会被分解为小块的物理内存。此时,系统中部署的业务如果需要连续的大块内存,则系统会先进入耗时较长的内存规整流程,进而会引起系统性能抖动。一般情况下,内存碎片化会产生类似于如下所示的内核堆栈信息。

0xffffffff8118f9cb compaction_alloc  ([kernel.kallsyms])
0xffffffff811c88a9 migrate_pages  ([kernel.kallsyms])
0xffffffff811901ee compact_zone  ([kernel.kallsyms])
0xffffffff8119041b compact_zone_order  ([kernel.kallsyms])
0xffffffff81190735 try_to_compact_pages  ([kernel.kallsyms])
0xffffffff81631cb4 __alloc_pages_direct_compact  ([kernel.kallsyms])
0xffffffff811741d5 __alloc_pages_nodemask  ([kernel.kallsyms])
0xffffffff811b5a79 alloc_pages_current  ([kernel.kallsyms])
0xffffffff811c0005 new_slab  ([kernel.kallsyms])
0xffffffff81633848 __slab_alloc  ([kernel.kallsyms])
0xffffffff811c5291 __kmalloc_node_track_caller  ([kernel.kallsyms])
0xffffffff8151a8c1 __kmalloc_reserve.isra.30  ([kernel.kallsyms])
0xffffffff8151b7cd alloc_sib  ([kernel.kallsyms])
0xffffffff815779e9 sk_stream_alloc_skb  ([kernel.kallsyms])
0xffffffff8157872d tcp_sendmsg  ([kernel.kallsyms])
0xffffffff815a26b4 inet_sendmsg  ([kernel.kallsyms])
0xffffffff81511017 sock_aio_write  ([kernel.kallsyms])
0xffffffff811df729 do_sync_readv_writev  ([kernel.kallsyms])
0xffffffff811e0cfe do_readv_writev  ([kernel.kallsyms])

解决方案

应对Linux内存碎片化,您可以采取如下措施:

  • 调整min水位线

    多数情况下阿里云建议您将min水位线设置为总内存的1%~3%。推荐您设置为总内存的2%,当内存资源紧张时,提前进入异步回收。调整min水位线的命令如下:

    sysctl -w vm.min_free_kbytes = memtotal_kbytes * 2%

    其中,变量memtotal_kbytes * 2%表示当前实例内总内存的2%对应的内存大小。

  • 调整min水位线和low水位线之间的差值

    您可以通过内核的watermark_scale_factor调整min水位线和low水位线之间的差值,以应对业务突发申请内存的情况。watermark_scale_factor的默认值为总内存的0.1%,最小值(即min水位线和low水位线之间的最小差值)为0.5*min水位线。调整watermark_scale_factor的命令如下:

    sysctl -w vm.watermark_scale_factor = value

    其中,变量value为您手动设置的min水位线和low水位线之间的差值。

  • 定期进行内存规整

    您可以在业务空闲时段,主动触发异步内存规整。触发命令如下:

    echo 1 > /proc/sys/vm/compact_memory
  • 定期手动释放缓存

    以上措施均不能有效应对内存碎片化时,您还可以在业务空闲时段执行释放缓存(drop cache)的操作,然后内存会重新分配。释放缓存是避免内存碎片化的有效措施,但在执行释放缓存时会出现短时间的系统性能抖动。手动释放缓存的命令如下:

    echo 3 > /proc/sys/vm/drop_caches