Использование cgexec vs cgroup.procs для учета памяти с использованием 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вставке PID JVM в файл cgroup.procs cgroup. Похоже, контрольные группы сообщают об использовании памяти процессом после его помещения в контрольную группу.

Как cgroup учитывает память? Похоже, что при использовании cgexec учитывается потребление JVM. С другой стороны, при запуске JVM вне контрольной группы и перемещении ее в нее позже путем записи PID в файл cgroup.procs потребление памяти, указанное в memory.usage_in_bytes, остается нулевым, пока я не нажму Enter, и потребление не увеличится до 1024 * 1024 * 4, как и ожидалось. .

Кроме того, потребление памяти, о котором сообщает cgroups, не полностью совпадает с потреблением памяти, о котором сообщает, например, top.

Изменить: создал следующую программу на C и использовал ее для тестирования. Я вижу те же результаты. При использовании cgclassify использование памяти остается равным 0 до тех пор, пока не будет нажата клавиша ввода. С другой стороны, при использовании cgexec использование памяти > 0 перед нажатием Enter.

#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);
}

person AlexBrand    schedule 17.11.2015    source источник
comment
Попробуйте написать то же самое на C или C++.   -  person Basile Starynkevitch    schedule 20.11.2015
comment
@BasileStarynkevitch Спасибо за ваше предложение. Наконец-то я смог обойти и написать образец программы на C. Я вижу те же результаты... есть идеи?   -  person AlexBrand    schedule 30.11.2015


Ответы (1)


Когда вы выделяете страницу и она выгружается процессом, выделенная память помечается идентификатором, сообщающим ядру, к какой конкретной контрольной группе контроллера памяти принадлежит эта память (очевидно, память также будет принадлежать любому родителю контрольной группы).

Когда вы переносите процесс в новую контрольную группу, уже выделенная память не меняет своего тега. Было бы очень дорого «перетегнуть» все, и это даже не имело бы смысла (предположим, что страница используется двумя процессами, и вы переносите только один в другую контрольную группу. Каким должен быть «новый» тег? Теперь он используется двумя процессами в разных cgroups...)

Итак, если вы находитесь в контрольной группе /sys/fs/cgroup/memory (т. е. идентификатор вашей группы задач указан в /sys/fs/cgroup/memory/tasks, а не в файле задач каких-либо дочерних элементов этой контрольной группы), все, что вы выделяете, учитывается в отношении этой контрольной группы и только этой контрольной группы.

При переходе на другую контрольную группу (или дочернюю контрольную группу) только новые выделения памяти помечаются как принадлежащие этой новой контрольной группе.

cgexec запустит JVM в контрольной группе, поэтому все, что выделено во время инициализации, уже будет принадлежать контрольной группе, созданной специально для того, что вы выполняете.

Если вы запускаете JVM в корневой контрольной группе для контроллера памяти, то все, что выделено и затронуто при инициализации JVM, будет принадлежать корневой контрольной группе.

После того, как вы перенесете JVM в свою собственную закрытую контрольную группу (с любым механизмом) и затем выделите и коснитесь некоторых страниц, очевидно, что они будут принадлежать новой контрольной группе.

person Alexis Cousein    schedule 15.01.2016
comment
Другими словами, если вы хотите, чтобы вся память учитывалась в контрольной группе контроллера памяти для конкретного приложения, вам следует создать контрольную группу и переместиться в эту контрольную группу до вы разветвляете, чтобы запустить приложение. --- Чтобы сделать это вручную, просто создайте каталог в корневой группе, а затем в оболочке, например. echo $$ › /sys/fs/cgroup/memory/my_private_cgroup/tasks. Если вы запустите свою JVM из той оболочки (которая сейчас находится в контрольной группе my_private_cgroup), она с самого начала будет находиться в правильной контрольной группе. - person Alexis Cousein; 15.01.2016
comment
Спасибо Алексис. Есть ли где-нибудь документация по этому поводу? Я ничего не нашел в memory.txt - person AlexBrand; 30.01.2016