Alibaba Cloud Linux 2在内核版本4.19.91-24.al7增加了THP reclaim功能,用于解决因透明大页THP带来的内存问题(例如OOM)。本文介绍实现THP reclaim功能的接口,以及接口的示例操作说明。

背景信息

Linux操作系统的内存采用分页管理模式,其中的THP(Transparent Huge Page)是指内核中2 MiB或1 GiB大小的大页面(普通的小页面大小为4 KiB),一般被称为透明大页。基于应用程序使用到的内存范围越来越大,地址转换产生的开销变得不可忽视。因此,当服务器上的应用程序申请内存时,内核会根据进程的实际情况动态分配透明大页,透明大页可以减少TLB(Translation Lookaside Buffer) Miss的出现概率,从而提升应用程序的性能。

THP在带来性能提升的同时,也产生了一定副作用,即可能产生Memory bloating(内存膨胀)问题。该问题产生的原因说明:透明大页申请释放的粒度为2 MiB(即512个小页面),相较于小页面,透明大页更容易产生内存碎片,进而导致内核分配的内存容量大于实际需要的内存容量。

Memory bloating可能引发OOM(Out Of Memory)。例如,一个应用程序实际需要使用2个小页面,即8 KiB内存,但是内核分配了1个透明大页。此时,除去应用程序实际需要的内存(2个小页面),透明大页剩下的内存(510个小页面)大小均为0。最终可能会因RSS(Resident Set Size)内存用量增加而导致OOM。

为解决THP可能引发的内存问题。Alibaba Cloud Linux 2增加了memcg粒度的THP reclaim功能。在内核回收内存时,该功能会把透明大页拆分为小页面,并将其中的全零页面(zero subpage)回收,从而避免内存的快速膨胀引发OOM。但您需要注意,由于THP reclaim功能会将透明大页拆分为小页面,所以相较于2 MiB大小的透明大页,THP reclaim功能会在一定程度上造成内存性能的回退。

接口说明

实现THP reclaim功能的接口说明,如下表所述:
接口 说明
memory.thp_reclaim 开启或关闭THP reclaim功能。支持以下配置项:
  • reclaim:开启THP reclaim功能。
  • swap:预留配置项,目前暂无实际功能。
  • disable:关闭THP reclaim功能。
THP reclaim功能默认为关闭状态,即接口默认值为disable。
memory.thp_reclaim_stat 查看THP reclaim功能当前的状态。接口内的参数说明如下:
  • queue_length:当前被THP reclaim管理的透明大页数量。
  • split_hugepage:累计拆分的透明大页数量。
  • reclaim_subpage:累计回收的全零页面(zero subpage)数量。
该接口的参数值按照NUMA node的顺序(node0、node1)从左到右排列。
memory.thp_reclaim_ctrl 控制THP reclaim功能的触发机制。目前支持以下配置项:
  • threshold:默认值为16。表示当透明大页中的全零页面(zero subpage)数量超过该值时,会触发THP reclaim功能。
  • reclaim:用于主动触发THP reclaim功能。
/sys/kernel/mm/transparent_hugepage/reclaim THP reclaim功能的全局配置接口。支持以下配置项:
  • memcg:该接口的默认配置项。表示每个memory cgroup按照各自的配置开启或关闭THP reclaim,即以每个memory cgroup中的memory.thp_reclaim接口配置为准。
  • reclaim:强制开启所有memory cgroup的THP reclaim功能。
  • swap:预留配置项,目前暂无实际功能。
  • disable:强制关闭所有memory cgroup的THP reclaim功能。

接口配置的操作说明

本示例操作中,将创建一个名为test的memory cgroup,以test为例介绍THP reclaim接口的操作说明。

  1. 运行以下命令,创建一个名为test的memory cgroup。
    mkdir /sys/fs/cgroup/memory/test/
  2. 运行以下命令,开启test的THP reclaim功能。
    echo reclaim > /sys/fs/cgroup/memory/test/memory.thp_reclaim
  3. 运行以下命令,确认test的THP reclaim功能成功开启。
    cat /sys/fs/cgroup/memory/test/memory.thp_reclaim
    返回结果如下图所示,被[]包裹的配置项为生效配置项,[reclaim]表示test的THP reclaim功能已开启。开启THP功能
  4. 运行以下命令,通过THP reclaim功能的全局配置接口强制开启THP reclaim功能。
    echo reclaim > /sys/kernel/mm/transparent_hugepage/reclaim
    如果您想强制关闭THP reclaim功能,可运行以下命令:
    echo disable > /sys/kernel/mm/transparent_hugepage/reclaim
    说明 THP reclaim功能的全局配置接口/sys/kernel/mm/transparent_hugepage/reclaim在设置为强制开启(reclaim)或强制关闭(disable)时,接口生效的优先级会高于各个memory cgroup中的memory.thp_reclaim接口,但不会影响各个memory cgroup中memory.thp_reclaim接口的配置。
  5. 运行以下命令,配置testmemory.thp_reclaim_ctrl接口的threshold配置项。
    echo "threshold 32" >  /sys/fs/cgroup/memory/test/memory.thp_reclaim_ctrl
    此时,如果透明大页中的全零页面数量超过32,则会触发THP reclaim的全零页面回收功能。
  6. 主动触发THP reclaim的全零页面回收功能。
    主动触发后,THP reclaim会把所有超过threshold配置的全零页面回收。配置项reclaim存在以下配置方式:
    说明 该配置项的调用方式为同步调用,并且为只写配置项,即您只能向memory.thp_reclaim_ctrl接口写入reclaim以主动触发THP reclaim的全零页面回收功能,但不能通过cat命令查看到reclaim配置项。
    • 运行以下命令,会主动触发当前memory cgroup的THP reclaim的全零页面回收功能。
      echo "reclaim 1" >  /sys/fs/cgroup/memory/test/memory.thp_reclaim_ctrl
    • 运行以下命令,会主动递归触发当前memory cgroup以及该cgroup下所有子cgroup的THP reclaim的全零页面回收功能。
      echo "reclaim 2" >  /sys/fs/cgroup/memory/test/memory.thp_reclaim_ctrl
      除了通过reclaim主动触发THP reclaim的全零页面回收功能。THP reclaim还会伴随内存回收而触发:
      • 内存出现OOM时,会触发THP reclaim的全零页面回收功能。
      • 当memory cgroup触发memory后台异步回收时,会触发THP reclaim的全零页面回收功能。关于memory后台异步回收的更多信息,请参见Memcg后台异步回收
  7. 运行以下命令,查看test的THP reclaim功能状态。
    cat  /sys/fs/cgroup/memory/test/memory.thp_reclaim_stat
    返回结果示例如下:
    queue_length        14
    split_hugepage     523
    reclaim_subpage 256207

C语言代码测试样例

本章节基于C语言提供了进程申请透明大页的代码测试样例。您可以通过以下测试样例,查看到THP reclaim功能在开启与关闭时的区别。

  1. 运行以下命令,为内存使用量限制接口memory.limit_in_bytes设置1 GiB的限制。
    echo 1G > /sys/fs/cgroup/memory/test/memory.limit_in_bytes
    设置后,您可以运行以下命令查看memory.limit_in_bytes接口的值。
    cat /sys/fs/cgroup/memory/test/memory.limit_in_bytes
    查看结果如下图所示:异步回收机制
  2. 运行以下命令,关闭memcg后台异步回收功能。
    关于memory后台异步回收的更多信息,请参见Memcg后台异步回收
    echo 0 > /sys/fs/cgroup/memory/test/memory.wmark_ratio
  3. 分别在开启或关闭THP reclaim功能的前提下,运行以下C语言代码进行测试,并查看测试结果。
    // 申请1 G内存(即512个透明大页),其中10个透明大页包含部分全零页面。
    #define HUGEPAGE_SIZE 4096 * 512
    int main()
    {
            int i, thp = 512;
            char *addr;
            posix_memalign((void **)&addr, HUGEPAGE_SIZE, HUGEPAGE_SIZE * thp);
    
            for (i = 0; i < 10; i++) {
                    memset(addr, 0xc, HUGEPAGE_SIZE >> 1);
                    addr += HUGEPAGE_SIZE;
            }
    
            for (; i < thp; i++) {
                    memset(addr, 0xc, HUGEPAGE_SIZE);
                    addr += HUGEPAGE_SIZE;
            }
    
            pause();
            return 0;
    }
    测试结果根据THP reclaim功能的开启状态有所不同:
    • 在THP reclaim功能关闭时,进程申请最后一个透明大页会发生OOM。
    • 在THP reclaim功能开启时,进程申请透明大页过程中,THP reclaim会把前序申请的透明大页拆分为小页,并回收其中的全零页面,最终不会发生OOM。