
# 内核memory的多级内存回收策略
#### 需求背景
在容器高密度混合部署场景中，IO读写较多的离线业务消耗大量page cache，导致系统空闲内存降低，达到全局空闲内存水位线后触发全局内存回收，使得在线任务申请内存时进入内存回收的慢路径，引发时延抖动。
为解决此问题，HCE 2.0和HCE 3.0新增支持多级内存回收策略。申请内存时，设置内存警示值，可触发内存回收任务，保证可用内存空间；回收内存时，设置多级内存保护水位线，保护任务可用性。
#### 约束与限制
memory.min和memory.low只在叶子节点生效。创建memory cgroup时，会将父节点的memory.min和memory.low清零。
#### 多级内存回收接口说明
memory.min、memory.low和memory.high接口在非根的memory cgroup下面默认存在，可以向文件内写值配置，也可以读取当前配置。合理的取值大小顺序为memory.min≤memory.low\<memory.high，三者可独立使用，也可联合使用。
内存回收机制如下图。
图1内存回收机制   
![](https://support.huaweicloud.com/usermanual-hce/zh-cn_image_0000002512128927.png "点击放大")
表1接口参数说明 
| 接口          | 说明                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
|:---|:---|
| memory.min  | 硬保护内存保护值，默认值为0。系统没有可回收内存的时候，也不会回收在该值边界及以下的内存。读写说明如下： - 读该接口可以查看硬保护内存大小，单位为byte。  - 写该接口可以设置硬保护内存大小，单位不做限制。  - 配置范围：0-memory.limit_in_bytes。                                                                                                                     |
| memory.low  | 尽力而为的内存保护值，默认值为0。 系统优先回收未被保护的cgroup组的内存。如果内存还不足，再回收memory.min到memory.low之间的内存。 读写说明如下： - 读该接口可以查看Best-effort内存保护值，单位为byte。  - 写该接口可以设置Best-effort内存保护值，单位不做限制。  - 配置范围：0-memory.limit_in_bytes。   |
| memory.high | 内存回收警示值，默认为max。当cgroup组内存使用量达到high值，会触发对该cgroup及子节点的同步内存回收任务，将内存尽量限制在high之下，避免触发memory.limit限制导致OOM。读写说明如下： - 读该接口可以查看Throttle limit内存保护值，单位为byte。  - 写该接口可以设置Throttle limit内存保护值，单位不做限制。  - 配置范围：0-memory.limit_in_bytes。                                  |
   
#### 接口示例
按如下所示创建6个cgroup节点A、B、C、D、E、F，配置memory.min接口，示例说明多级内存回收策略。
图2多级内存回收策略   
![](https://support.huaweicloud.com/usermanual-hce/zh-cn_image_0000002479929030.png "点击放大")
表2数据规划 
| cgroup组 | memory.limit_in_bytes | memory.min | memory.usage_in_bytes |
|:---|:---|:---|:---|
| A       | 200M                  | 0          | -                     |
| B       | -                     | 0          | -                     |
| C       | -                     | 75M        | 50M                   |
| D       | -                     | 25M        | 50M                   |
| E       | -                     | 0          | 50M                   |
| F       | -                     | 125M       | -                     |
   
1. 创建cgroup节点A，并配置memory.limit_in_bytes=200M。 注意cgroupV2与V1默认层级略有差异，以下是v1示例：
   ```
   mkdir /sys/fs/cgroup/memory/A
   echo 200M > /sys/fs/cgroup/memory/A/memory.limit_in_bytes
   ```
   
2. 创建cgroup节点B。
   ```
   mkdir /sys/fs/cgroup/memory/A/B
   ```
   
3. 创建cgroup节点C，并配置memory.min=75M，在当前节点创建申请50M cache的进程。
   ```
   mkdir /sys/fs/cgroup/memory/A/B/C
   echo 75M > /sys/fs/cgroup/memory/A/B/C/memory.min
   ```
   
4. 创建cgroup节点D，并配置memory.min=25M，在当前节点创建申请50M cache的进程。
   ```
   mkdir /sys/fs/cgroup/memory/A/B/D
   echo 25M > /sys/fs/cgroup/memory/A/B/D/memory.min
   ```
   
5. 创建cgroup节点E，并配置memory.min=0，在当前节点创建申请50M cache的进程。
   ```
   mkdir /sys/fs/cgroup/memory/A/B/E
   ```
   
6. 创建cgroup节点F，并配置memory.min=125M，申请125M保护内存，触发内存回收。
   ```
   mkdir /sys/fs/cgroup/memory/A/F
   echo 125M > /sys/fs/cgroup/memory/A/F/memory.min
   ```
   
返回结果：
C节点memory.min=75M，memory.usage_in_bytes=50M。
D节点memory.min=25M，memory.usage_in_bytes=25M。
E节点memory.min=0，memory.usage_in_bytes=0。
B节点memory.usage_in_bytes=75M。
