Страницы

Поиск по вопросам

Показаны сообщения с ярлыком memory-management. Показать все сообщения
Показаны сообщения с ярлыком memory-management. Показать все сообщения

суббота, 7 марта 2020 г.

Зачем windows позволяет создавать приватные кучи?

#winapi #memory #memory_management

Каждый процесс в ОС Windows имеет по умолчанию свою кучу, размер которой в будущем
может быть изменен. Более того, каждый процесс способен так же создавать кучу с определенным,
наперед заданным начальным и максимальным размером (такая куча будет называться приватной).
Зачем такая возможность существует в Windows? Чтобы оптимизировать реаллокации? Но
тогда не достаточно ли в таком случае использования обычной кучи по умолчанию?
    


Ответы

Ответ 1



При создании кучи можно задавать наличие следующих параметров: HEAP_CREATE_ENABLE_EXECUTE — разместить кучу на страницах с правом на исполнение. Подобное можно использовать для JIT-компиляции небольших фрагментов кода, когда выделение целой 4КБ страницы на фрагмент было бы излишним. HEAP_GENERATE_EXCEPTIONS — выбрасывать SEH-исключения из функций управления кучей. HEAP_NO_SERIALIZE — разменять поддержку многоточности на скорость выделения/высвобождения блоков кучи. В куче по умолчанию ничего из вышеперечисленного не включено.

вторник, 18 февраля 2020 г.

Выделение памяти в С++ и аварийное завершение программы

#cpp #memory_leaks #memory_management

Допустим, у меня есть класс, который я создаю в самом начале программы, в его конструкторе
я выделяю память под какие-то другие объекты с помощью операторов new, а в его деструкторе
вызываю delete. Насколько такая практика приемлема и что произойдёт, если во время
работы программы её аварийно завершить (скажем, прихлопнуть через диспетчер задач).
Спасибо.
    


Ответы

Ответ 1



Если Ваше приложение будет "прихлопнуто" или аварийно завершено, то обычная выделенная память будет подчищена. Да, деструктор не будет вызван, но какое это имеет значение, если приложение уже упало. Поэтому, если просто выделили память себе под массив, а потом подчистили - все ок. Но в современном с++ явный вызов new/delete считается моветоном. Обычные unique_ptr/shared_ptr решают 90% подобных проблем и нивелируют необходимость в теле деструктора. Другое дело, если приложение выделят какие-то общие ресурсы - shared memory или междупроцессорные мютексы/семафоры. Тут могут быть проблемы.

воскресенье, 16 февраля 2020 г.

Выделение памяти в С++ и аварийное завершение программы

#cpp #memory_leaks #memory_management

Допустим, у меня есть класс, который я создаю в самом начале программы, в его конструкторе
я выделяю память под какие-то другие объекты с помощью операторов new, а в его деструкторе
вызываю delete. Насколько такая практика приемлема и что произойдёт, если во время
работы программы её аварийно завершить (скажем, прихлопнуть через диспетчер задач).
Спасибо.
    


Ответы

Ответ 1



Если Ваше приложение будет "прихлопнуто" или аварийно завершено, то обычная выделенная память будет подчищена. Да, деструктор не будет вызван, но какое это имеет значение, если приложение уже упало. Поэтому, если просто выделили память себе под массив, а потом подчистили - все ок. Но в современном с++ явный вызов new/delete считается моветоном. Обычные unique_ptr/shared_ptr решают 90% подобных проблем и нивелируют необходимость в теле деструктора. Другое дело, если приложение выделят какие-то общие ресурсы - shared memory или междупроцессорные мютексы/семафоры. Тут могут быть проблемы.

среда, 5 февраля 2020 г.

Правильное выделение памяти под структуры

#c #memory #struct #memory_management

Всем, доброго времени суток! Продолжаю своё медленное, но упорное знакомство с СИ.

Как правильней всего выделить память под структуру, в которой есть поле... Не уверен
что правильный термин, но что-то вроде динамического поля. Смотрим код ниже.

typedef struct
{
   int count;
   char **arrays;
} _array_strs;

_array_strs *getArrayFromFile()
{
   FILE *fd = getFile(STORAGE, "r");
   char sym;
   char **arrays = (char **)getPtrMatrix(&sym, COUNT_WORDS, BUFF_WORD); 

   struct _array_strs *array_strs = (struct _array_strs *)calloc(sizeof(arrays) +
sizeof(int), sizeof(struct array_strs));
   int i = 0;
   while (!feof(fd)) {
      fgets(arrays[i], BUFF_WORD, fd);
      i++;
   }

   array_strs->count = i;
   array_strs->arrays = arrays;

   fclose(fd);

   return array_strs;
}


Пару часов убил, много противоречивого нагуглил. Как не пробовал, но компилятор постоянно
мною недоволен. При таком варианте кода, его ругательства выглядят следующим образом:

src/search.c: In function ‘getArrayFromFile’:
src/search.c:32:101: error: invalid application of ‘sizeof’ to incomplete type ‘struct
array_strs’
 struct _array_strs *array_strs = (struct _array_strs *)calloc(sizeof(arrays) + sizeof(int),
sizeof(struct array_strs));
                                                                                
                ^
src/search.c:39:12: error: dereferencing pointer to incomplete type ‘struct _array_strs’
 array_strs->count = i;
        ^
src/search.c:44:9: warning: return from incompatible pointer type [-Wincompatible-pointer-types]
 return array_strs;


Буду рад любой помощи компетентных специалистов. Всем добра!
    


Ответы

Ответ 1



Во-первых, у вас в программе нет такого типа, как struct _array_strs. Поэтому "выделить под него память" у вас не получится. При таком объявлении типа, как у вас, тип вашей структуры называется _array_strs, а не struct _array_strs. Чтобы получить тип struct _array_strs объявление должно быть таким struct _array_strs { int count; char **arrays; }; Вы можете совместить оба варианта typedef struct _array_strs { int count; char **arrays; } _array_strs; что сделает _array_strs и struct _array_strs синонимами. Однако использовать эти имена вперемешку не рекомендуется. Выберите одно. Что характерно, в объявлении функции getArrayFromFile у вас использовано правильное имя типа для вашего исходного варианта (просто _array_strs), но затем в коде вдруг непонятно откуда полез какой-то struct _array_strs. Почему? В общем, сначала определитесь, как именно вы хотите называть свой тип и соответствующим образом исправьте его объявление и ссылки на его название в коде. Во-вторых, в текущей версии под сам объект _array_strs выделить память несложно _array_strs *array_strs = malloc(sizeof *array_strs); ... array_strs->count = i; array_strs->arrays = arrays; и все. У вас же там под calloc понаписана какая-то бессмысленная каша. Если вам зачем-то хочется воспользоваться именно calloc, то _array_strs *array_strs = calloc(1, sizeof *array_strs); ... Имейте в виду, что ваше "динамическое поле arrays" это никакое не "динамическое поле", а обычный указатель. На выделение памяти для самой структуры оно никак не влияет вообще. В вашем коде подразумевается, что память для char **arrays выделена где-то внутри getPtrMatrix и выделена правильно. Не ясно только, почему на результат getPtrMatrix сделан явный каст (char **). Почему? В-третьих, в языке С объявления функции со списком параметров () являются deprecated. Возьмите в привычку писать (void). Хотя функция без параметров - это в большинстве случаев странно. P.S. Ну и, конечно, "классика жанра" - цикл с предусловием while (!feof(fd)), который приведет к вопросам типа "почему у меня array_strs->count на единицу больше, чем надо".

понедельник, 6 января 2020 г.

Segmentation fault при обращении к указателю

#linux #c #указатели #malloc #memory_management

Решил для развития кругозора написать собственную имплементацию аллокатора памяти
под Linux, но при написании столкнулся со следующей проблемой. 

Вызов sbrk() возвращает указатель на начало выделенной памяти, затем я этот указатель
привожу к указателю на структуру Hdr. Но когда я пытаюсь задать значение переменной
по этому указателю, программа вылетает с Segmentation error. Чутьё подсказывает, что
я делаю глупость, ведь структуру нужно инициализировать прежде чем использовать, но
я не знаю как в данной ситуации правильно это сделать, учитывая, что память под структуру
уже выделена вызовом sbrk().

Заранее благодарен за любую помощь!

struct Hdr {
  int is_avaible;
  int size;
};

void *malloc(long memory_size) {
  void *memory_location;
  struct Hdr *current_location_struct;
  ...
  memory_location = sbrk(memory_size);
  if (memory_location == NULL)
    return;
  current_location_struct = (struct Hdr *)memory_location;
  current_location_struct->is_available = 0;
  ...
}

    


Ответы

Ответ 1



Давайте посмотрим как работает sbrk(). Схематично: void *sbrk(intptr_t increment) { void *heap_ptr = current_heap_end; if( increment > 0 ) { if( !expand_process_heap( increment ) ) { heap_ptr = (void *)-1; } } else if( increment < 0 ) { heap_ptr = reduce_process_heap( -increment ); } return heap_ptr; } Рассмотрим с конца. Если increment равен 0, то возвращаем текущий адрес конца кучи. Если отрицателен, то уменьшаем кучу и возвращаем адрес нового её конца. И если он положителен, то увеличиваем кучу и возвращаем старое его значение. Вот как раз пункт 3 ошибочно многими (не только начинающими программистами, но и книгописателями!) воспринимается как некий способ выделить память. Формально - да, как бы "память выделяется", как раз размером increment байтов. Но на деле эту память использовать по своему усмотрению напрямую нельзя! Потому что пытаясь туда что-то записать вы "сбиваете настройки" системному менеджеру памяти, что закономерно приводит к краху программы. Конечно, в случае реальной программы, а не в учебных примерах, в которых после использования sbrk() и вывода каких-то демо-значений в программе больше ничего не происходит. P.S. Кстати, если бы вы перед чтением сомнительной :) литературы прочитали man sbrk, то обратили бы внимание на следующее: On success, sbrk() returns the previous program break. (If the break was increased, then this value is a pointer to the start of the newly allocated memory). On error, (void *) -1 is returned, and errno is set to ENOMEM. P.P.S. Я уж не говорю о том, что заменить библиотечный malloc() своей реализацией - это не просто выстрелить себе в ногу, это усесться на связку гранат, облиться бензином и подорвать их.

Ответ 2



Что-то меня зацепило всеобщее недоверие к реализации своего malloc через sbrk. Вот очень простая (по сути дурацкая), но работающая программка, просто для демонстрации возможности работы с sbrk(). Поскольку вся группа функций (malloc/calloc/realloc/free) используется библиотечным софтом совместно, то реализовывать один лишь malloc нельзя. #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include static void *start; void * get_memstart () { return start; } // size multiples 16, allocated to bound multiples 16 struct bhdr { struct hdr { size_t bsize; uint cnt; char is_free; } hdr; char pad[(16 - sizeof(struct hdr)) & 15]; }; // each malloc returns new sbrk block void * malloc (size_t size) { static uint cnt = 0; const char *mtxt = "mmalloc\n"; if (write(1, mtxt, strlen(mtxt))) cnt++; if (!size) return 0; // increase space to mutilpe 16 for good bounding struct bhdr *p = (typeof(p))sbrk((15 + size + sizeof(*p)) & ~15); if ((long)p == -1L) return 0; if (!start) start = p; p->hdr.bsize = size; p->hdr.cnt = cnt; p->hdr.is_free = 0; return p + 1; } void * calloc (size_t nmemb, size_t size) { const char *mtxt = "mcalloc\n"; void *p = 0; if (write(1, mtxt, strlen(mtxt))) { p = malloc(nmemb * size); if (p) memset(p, 0, nmemb * size); } return p; } // mark early allocated block as free if bound is multiple 16 void free (void *ptr) { const char *mtxt = "mfree\n"; if (write(1, mtxt, strlen(mtxt))) if (!ptr || (long)ptr & 15) return; struct bhdr *p = (typeof(p))ptr; p[-1].hdr.is_free = 1; } // if new size greater than old size returns copy in new block void * realloc (void *ptr, size_t size) { const char *mtxt = "mrealloc\n"; if (write(1, mtxt, strlen(mtxt))) if (!ptr) return malloc(size); if ((long)ptr & 15) return 0; if (!size) { free(ptr); return (void *)-1L; } struct bhdr *p = (typeof(p))ptr; size_t tsz = p[-1].hdr.bsize; if (tsz >= size) return ptr; void *nptr = malloc(size); if (nptr) memcpy(nptr, ptr, tsz); return nptr; } int main (int ac, char *av[]) { struct bhdr b; printf("Hello, sbrk/malloc!\nbhdr: %d pad: %d hdr: %d\n", (int)sizeof(b), (int)sizeof(b.pad), (int)sizeof(b.hdr)); char *s = 0; size_t n; errno = 0; while ((fputs("Enter: ", stdout), getline(&s, &n, stdin)) > 0) { printf("read %s", s); if (*s == '.') { puts("New line mem"); free(s); s = 0; } } perror("stdin"); return fcloseall(); } Все "странности" кода с write() связаны с раздражающими (идиотскими) warnings gcc/g++ (g++.real (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0) вызываемым с ключами оптимизации (-O...) когда результат write не используется.

суббота, 7 декабря 2019 г.

Хорошая книга по работе с памятью в C++

#cpp #memory_management

Ищу хорошую книгу или серию статей, которая описала бы различные нюансы работы с
памятью в C++ (в каких-то аспектах язык может не иметь значения, но ориентируюсь именно
на этот язык программирования). Хотелось бы знать как "под капотом" работает распределение
памяти, хранение объектов и данных в памяти.

Описание вопроса довольно расплывчатое. Привожу пример вопросов, который меня интересует:


Когда вызывается new/delete как программа/ОС хранит информацию о выделенные участках
памяти. Как виртуальное адресное пространство соотносится с реальным адресным пространством.
Что из себя представляет класс в памяти. А если класс наследуется от двух других
классов с виртуальными функциями.
Exception-ы в конструкторах/деcтрукторах и особенности для многопоточных приложений.
Как реализуется Exception на низком уровне.
Оптимизация выделения памяти. Собственный аллокатор памяти.
Создание/удаление объектов в программе с несколькими модулями.


Возможно эти вопросы будут отражены в разных книгах, буду рад любым. Насчёт специфики
ОС и разных компиляторов: интересны любые варианты.
    


Ответы

Ответ 1



Про механизмы распределения памяти, потоки и межпроцессную/межпоточную синхронизацию нужно читать книги об устройстве операционных систем. Можно порекомендовать: Таненбаум, Бос. Современные операционные системы Дейтел Х.М. Операционные системы. Том 1. Основы и принципы. Таненбаум Э.С., Вудхалл А.С. Операционные системы. Разработка и реализация Описание реализации принципов работы операционных систем можно почитать в Лав. Ядро Linux: описание процесса разработки и Labrosse. µC/OS-II: The Real-Time Kernel Об алгоритмах распределения памяти рассказывается также в Ахо, Хопкрофт, Ульман. Структуры данных и алгоритмы Написание своих распределителей памяти обсуждается в Джосаттис. Стандартная библиотека C++: справочное руководство. Также этот вопрос затронут в Керниган, Ритчи. Язык программирования C Разработке многопоточных приложений на C++11 посвящена книга Уильямс. Параллельное программирование на С++ в действии Еще есть книга о "внутреннем устройстве" C++: Lippman, Inside the C++ Object Model. Она довольно старая - возможно там описаны не все интересующие темы.

Ответ 2



Одной книги покрывающей всё это, разумеется,— нет. По первому вопросу можно почитать Structured Computer Organization и Modern Operating Systems По второму книги есть вряд ли, т.к. область весьма специфичная и подвержена изменениям, тут лучше искать статьи. У меня есть две статьи на эту тему: Размещение объектов. Часть 1: Основы и Размещение объектов. Часть 2: Виртуальность По третьему вопросу книг читать не надо: исключения в конструкторе это нормально, деструктор исключения покидать не должны. По умолчанию, с C++11, исключение, которое покинет деструктор, вызовет std::terminate и приложение будет завершено. Многопоточность не имеет отношения ни к деструктору, ни к конструктору. Они не могут быть одновременно выполнены в разных потоках(если конечно не вызвать их явно для глобального объекта, но это экзотика) По четвёртому вопросу тот же Танненбаум, вышеупомянутый, для общего развития, а потом статьи в интернете. Но Вы не хотите писать свой аллокатор, правда. Вот неплохое видео по аллокаторам, от Александреску. И вот кусок книги(Supplementary Chapter) от одного замечательного автора, я не читал это приложение к книге, но читал основную книгу — автор всё очень хорошо описывает. Правда на английском, и видео и книга. Пятый вопрос: тоже нужно искать статьи в интернете. Если вкратце: если не хотите иметь головную боль, то лучше освобождать память посредством того же модуля, что её выделял.

среда, 22 мая 2019 г.

Зачем windows позволяет создавать приватные кучи?

Каждый процесс в ОС Windows имеет по умолчанию свою кучу, размер которой в будущем может быть изменен. Более того, каждый процесс способен так же создавать кучу с определенным, наперед заданным начальным и максимальным размером (такая куча будет называться приватной). Зачем такая возможность существует в Windows? Чтобы оптимизировать реаллокации? Но тогда не достаточно ли в таком случае использования обычной кучи по умолчанию?


Ответ

При создании кучи можно задавать наличие следующих параметров:
HEAP_CREATE_ENABLE_EXECUTE — разместить кучу на страницах с правом на исполнение. Подобное можно использовать для JIT-компиляции небольших фрагментов кода, когда выделение целой 4КБ страницы на фрагмент было бы излишним. HEAP_GENERATE_EXCEPTIONS — выбрасывать SEH-исключения из функций управления кучей. HEAP_NO_SERIALIZE — разменять поддержку многоточности на скорость выделения/высвобождения блоков кучи.
В куче по умолчанию ничего из вышеперечисленного не включено.

среда, 10 октября 2018 г.

Хорошая книга по работе с памятью в C++

Ищу хорошую книгу или серию статей, которая описала бы различные нюансы работы с памятью в C++ (в каких-то аспектах язык может не иметь значения, но ориентируюсь именно на этот язык программирования). Хотелось бы знать как "под капотом" работает распределение памяти, хранение объектов и данных в памяти.
Описание вопроса довольно расплывчатое. Привожу пример вопросов, который меня интересует:
Когда вызывается new/delete как программа/ОС хранит информацию о выделенные участках памяти. Как виртуальное адресное пространство соотносится с реальным адресным пространством. Что из себя представляет класс в памяти. А если класс наследуется от двух других классов с виртуальными функциями. Exception-ы в конструкторах/деcтрукторах и особенности для многопоточных приложений. Как реализуется Exception на низком уровне. Оптимизация выделения памяти. Собственный аллокатор памяти. Создание/удаление объектов в программе с несколькими модулями.
Возможно эти вопросы будут отражены в разных книгах, буду рад любым. Насчёт специфики ОС и разных компиляторов: интересны любые варианты.


Ответ

Про механизмы распределения памяти, потоки и межпроцессную/межпоточную синхронизацию нужно читать книги об устройстве операционных систем. Можно порекомендовать:
Таненбаум, Бос. Современные операционные системы Дейтел Х.М. Операционные системы. Том 1. Основы и принципы. Таненбаум Э.С., Вудхалл А.С. Операционные системы. Разработка и реализация
Описание реализации принципов работы операционных систем можно почитать в Лав. Ядро Linux: описание процесса разработки и Labrosse. µC/OS-II: The Real-Time Kernel
Об алгоритмах распределения памяти рассказывается также в Ахо, Хопкрофт, Ульман. Структуры данных и алгоритмы
Написание своих распределителей памяти обсуждается в Джосаттис. Стандартная библиотека C++: справочное руководство. Также этот вопрос затронут в Керниган, Ритчи. Язык программирования C
Разработке многопоточных приложений на C++11 посвящена книга Уильямс. Параллельное программирование на С++ в действии
Еще есть книга о "внутреннем устройстве" C++: Lippman, Inside the C++ Object Model. Она довольно старая - возможно там описаны не все интересующие темы.