Проблема с функцией free () в C и утечками памяти - PullRequest
0 голосов
/ 26 апреля 2020

У меня проблема с освобождением памяти с помощью free () в C. Моя программа генерирует случайное генеалогическое дерево c с использованием матрицы. Эта матрица может быть очень большой в зависимости от количества членов семьи. Программа, казалось, работала нормально, пока я не решил создать более одного дерева. Я заметил, что генерация около 100 деревьев приводит к заполнению моей 8 ГБ ОЗУ! Я уверен, что смогу сделать лучший код, чтобы уменьшить потребность в памяти, но моя проблема остается. Я использую free () для освобождения памяти, и нет ошибки. Я установил Valgrind, чтобы увидеть, что происходит, и он говорит, что около 100 миллионов байт на дерево определенно потеряно. Это означает, что free () не работает нормально. Я не знаю, где проблема. Я связываю некоторые функции, которые, по моему мнению, связаны с проблемой.

typedef struct{
   int f_id;
   char f_name[L_NAMES];
   int generations;
   int n_members;
   type_people *members;
   int_mtx *mtx;
}type_family;

Структура выше предназначена для семьи.

typedef struct temp{
   int p_id;
   char name[L_NAMES];
   char f_name[L_NAMES];
   int generation;
   int n_sons;
   struct temp **sons; 
   int f_id;
   int sex;
   int age;
}type_people;

Это для членов.

typedef struct{
   int i;
   int j;
   int **val;
}int_mtx;

И матрица. В основном я вызываю функцию для инициализации дерева:

type_family *family_a;
family_a = malloc(sizeof(type_family));
family_a = init_family_n_gen(family_a, 6);

Это первая часть init_family_n_gen ():

type_family *init_family_n_gen(type_family *family, int n){
   ...
   family->members = malloc(max_people * sizeof(type_people));
   family->mtx = mtxcalloc(family->mtx, max_people, max_people - 1);
   ...

Этот код для mtxcallo c, который инициализирует матрица:

int_mtx *mtxcalloc(int_mtx *mtx, int i, int j){
   mtx = malloc(sizeof(int_mtx));
   mtx->i = i;
   mtx->j = j;
   mtx->val = malloc(i * sizeof(int *));
   for(int a = 0; a < i; a++){
      mtx->val[a] = malloc(j * sizeof(int));
      for(int b = 0; b < j; b++){
         mtx->val[a][b] = 0;
      }
   }
   return mtx;
 }

И для завершения код для освобождения семейства:

void free_family(type_family *family){
   for(int m = 0; m < family->n_members; m++){
     if(family->members[m].n_sons != 0){
        free(family->members[m].sons);
     }
   }
   mtxfree(family->mtx);
   free(family->members);
}

И один для освобождения матрицы:

void mtxfree(int_mtx *mtx){
   for(int i = 0; i < mtx->i; i++){
      free(mtx->val[i]);
   }
   free(mtx->val);
   free(mtx);
}

Снимок экрана с выводом Valgrind Поэтому я вызываю free_family (family_a) каждый раз, когда мне нужно восстановить семью, но память все еще увеличивается. (На фото выше число байтов станет 1 миллиард, если я регенерирую семью в 50 раз). Спасибо за поддержку!

EDITED

Я сделал минимальный воспроизводимый пример, который имитирует мой оригинальный код. Структуры и переменные одинаковы, но я изменил функции в соответствии с Weather Vane: все они недействительны, и я передаю им двойные **. Init_family_n_gen становится:

void init_family(type_family **f){
  type_family *family = malloc(sizeof(type_family));
  family->members = malloc(100 * sizeof(type_people));
  for(int m = 0; m < 100; m++){
     family->members[m].n_sons = 0;
  }
  mtxcalloc(&family->mtx, 100, 99);
  family->mtx->val[0][1] = 7;
  family->mtx->val[9][8] = 1;
  mtxrealloc(&family->mtx, 5, 4);
  *f = family;
}

Основным является:

type_family *family_a;
init_family(&family_a);
free_family(&family_a);

Единственное, что я добавил, - эта функция (правильный код?):

void mtxrealloc(int_mtx **mtx, int i, int j){
   (*mtx)->i = i;
   (*mtx)->j = j;
   (*mtx)->val = realloc((*mtx)->val, (*mtx)->i * sizeof(int *));
   for(int a = 0; a < (*mtx)->i; a++){
    (*mtx)->val[a] = realloc((*mtx)->val[a], (*mtx)->j * sizeof(int));
   }
}

Я заметил, что проблема возникает, когда я использую функцию reallo c, и я не могу понять, почему. Я связываю изображения Valgrind с и без функции mtxreallo c. (Я вижу, что есть также 48-байтовая утечка ...). Valgrind с reallo c Valgrind без reallo c Еще раз спасибо за вашу поддержку!

1 Ответ

0 голосов
/ 27 апреля 2020

This:

init_family(&family_a);

Заставляет этот код из mtxcalloc выполнить:

mtx->val = malloc(i * sizeof(int *));
for(int a = 0; a < i; a++){
   mtx->val[a] = malloc(j * sizeof(int));
   for(int b = 0; b < j; b++){
      mtx->val[a][b] = 0;
   }
}

, с i , j = 100, 99. То есть вы выделяете место для 100 указателей, а для каждого вы выделяете место для 99 int с. Затем они доступны через family_a->mtx.

Вскоре после этого вы делаете этот вызов:

 mtxrealloc(&family->mtx, 5, 4);

, который делает это, среди прочего:

   (*mtx)->val = realloc((*mtx)->val, (*mtx)->i * sizeof(int *));

При этом теряются все указатели с (*mtx)->val[5] по (*mtx)->val[99], , каждый из которых является единственным указателем на выделенное пространство, достаточное для 99 int с . В общем, достаточно места для 9405 int с, пока вы не выполните какие-либо вычисления с объектом, который вы готовите.

Неясно, почему вы перераспределили, просто чтобы немедленно (попытаться) освободить излишки, но возможно, это артефакт упрощения вашего кода. Было бы намного лучше найти способ заранее определить, сколько места вам нужно, а затем выделять только это в первую очередь. Но если вам нужно перераспределить эти конкретные данные, то вам нужно сначала освободить каждую из (*mtx)->val[x], которая будет потеряна. Конечно, если вы собираетесь перераспределить больше, вам нужно будет выделить / перераспределить все из (*mtx)->val[x].

...