使用 cgexec vs cgroup.procs 使用 cgroups 进行内存统计

Using cgexec vs cgroup.procs for memory accounting using cgroups

我 运行 昨天使用 cgroups 内存控制器遇到了一个有趣的情况。我一直认为cgroups报告的内存是进程的总内存消耗,但似乎不是这样。

我写了下面的Java程序来测试:

import java.util.Scanner;

class TestApp {

  public static void main(String args[]) {

    int[] arr;

    Scanner in = new Scanner(System.in);
    System.out.println("Press enter to allocate memory");
    in.nextLine();

    arr = new int[1024*1024];
    System.out.println("Allocated memory");
    while(true);
  }

}

当运行上述cgexec时,内存使用与echo将JVM的PID写入cgroup.procscgroup文件时有很大不同.似乎 cgroups 报告进程的内存使用情况 after 它已被放置在 cgroup 中。

cgroup是怎么占内存的?似乎在使用 cgexec 时考虑了 JVM 消耗。另一方面,当在 cgroup 外部启动 JVM 并稍后通过将 PID 写入 cgroup.procs 文件将其移入其中时,memory.usage_in_bytes 中报告的内存消耗保持为零,直到我按下回车键消耗量按预期上升到 1024 * 1024 * 4

此外,cgroups报告的内存消耗与top报告的内存消耗不完全相同,例如

编辑: 创建了以下 C 程序并将其用于测试。我看到了相同的结果。如果使用 cgclassify,内存利用率将保持为 0,直到按下回车键。另一方面,当使用 cgexec 时,在按回车之前内存利用率 > 0。

#include <stdio.h>
#include <stdlib.h>

int main() {

  printf("Press ENTER to consume memory\n");
  getchar();

  char *ptr = malloc(1024*1024);
  if (ptr == NULL) {
    printf("Out of memory");
    exit(1);
  }

  memset(ptr, 0, 1024*1024);

  printf("Press ENTER to quit\n");
  getchar();

  return(0);
}

当你分配一个页面并且它被一个进程调入时,分配的内存被标记一个标识符,告诉内核这个内存属于哪个特定的内存控制器cgroup(显然内存也属于任何父代cgroup 的)。

当您将进程迁移到新的 cgroup 时,已经 分配的内存不会更改其标签。 "retag" 一切都会非常昂贵,甚至没有意义(假设一个页面由两个进程共享,而您只将一个迁移到不同的 cgroup。"new" 标记会是什么需要吗?它现在被不同 cgroup 中的两个进程使用...)

因此,如果您坐在 /sys/fs/cgroup/memory cgroup 中(即您的任务组 ID 在 /sys/fs/cgroup/memory/tasks 中提及,而不是在该 cgroup 的任何子项的任务文件中),任何您分配的是根据 那个 cgroup 和那个 cgroup 计算的。

当您迁移到不同的 cgroup(或子 cgroup)时,只有 内存分配被标记为属于该新 cgroup。

cgexec 将在 中启动 JVM 一个 cgroup,因此在初始化时分配的任何内容都已经属于专门为您执行的内容创建的 cgroup。

如果您在内存控制器的根 cgroup 中启动 JVM,那么在初始化 JVM 时分配和接触的任何内容都将属于根 cgroup。

一旦您将 JVM 迁移到它自己的私有 cgroup(使用任一机制)并且 然后 您分配并接触了一些页面,那么显然这些页面将属于新的 cgroup。