1

昨天我在使用 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);
  }

}

使用 运行上述程序时,内存使用情况与将 JVM 的 PID 写入cgroup 的文件cgexec时有很大不同。似乎 cgroup 在将进程放入 cgroup后会报告进程的内存使用情况。echocgroup.procs

cgroup 如何占内存?似乎在使用时cgexec,JVM 的消耗被考虑在内。另一方面,当在 cgroup 之外启动 JVM 并稍后通过将 PID 写入cgroup.procs文件将其移动到其中时,报告的内存消耗memory.usage_in_bytes保持为零,直到我按 enter 并且消耗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);
}
4

1 回答 1

2

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

当您将进程迁移到新的 cgroup 时,分配的内存不会更改其标记。“重新标记”所有内容将非常昂贵,甚至没有意义(假设一个页面由两个进程共享,并且您只将一个迁移到不同的 cgroup。“新”标记需要是什么?它现在被不同 cgroup 中的两个进程使用...)

因此,如果您位于 /sys/fs/cgroup/memory cgroup 中(即,您的任务组 ID 在 /sys/fs/cgroup/memory/tasks 中提及,而不是在该 cgroup 的任何子项的任务文件中),您分配的任何内容都仅计入cgroup 和该 cgroup。

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

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

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

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

于 2016-01-15T10:28:51.070 回答