Как обнаружить и оценить фрагментацию кучи в моей программе на C++?

Я занимаюсь разработкой службы VC++ NT, которая должна работать непрерывно в течение многих месяцев. Он интенсивно использует кучу VC++. Очевидно, что в какой-то момент фрагментация кучи может привести к ее неисправности (думая, что ей не хватает памяти).

Какие тесты я могу запустить на своем сервисе, чтобы оценить степень его подверженности фрагментации кучи?

6 ответов

Вы получили пару ответов, в которых рассказывается о том, как предотвратить проблемы с фрагментацией кучи, но ни один из них на самом деле не ответил на ваш вопрос напрямую. Почти единственный способ оценить вероятность возникновения проблем с фрагментацией - это моделировать большое использование и измерить полученную фрагментацию.

Так как это сервис NT, моделирование месяцев использования в основном состоит из поспешного выполнения большого количества запросов. Скорее всего, вы можете отправлять запросы быстрее, чем обычно ожидается, поэтому вы, вероятно, можете смоделировать запросы на несколько месяцев всего за несколько часов, а может быть, даже меньше (в зависимости от скорости, с которой вы обычно ожидаете получать запросы).).

После того, как вы смоделируете многолетнюю работу (или даже когда вы это делаете), вам нужно посмотреть на кучу, чтобы увидеть, сколько фрагментации вы получаете. Это не легко, но обычно это возможно. Вы начнете с внедрения потока в сервисный процесс (поиск в Google по запросу "внедрение потока" или что-то в этом порядке должно получить достаточное количество информации). Затем вам нужно будет пройтись по куче, ища (в частности) свободные блоки, но слишком маленькие, чтобы удовлетворить большинство запросов. Предполагая, что вы используете MS VC++, вы обходите кучу с помощью _heapwalk, и она будет проходить через кучу, сообщая вам адрес, размер и состояние (свободно или используется) каждого блока в куче.

Одна последняя деталь: для того, чтобы это дало значимые результаты, как исполняемый файл, так и DLL, содержащая ваш внедренный поток, должны быть связаны с библиотекой времени выполнения в DLL. Это означает, что будет одна куча для всего процесса, поэтому ваш внедренный поток будет проходить кучу, используемую вашим сервисом. Если вы статически связываете стандартную библиотеку, DLL и служба будут иметь свою собственную кучу. DLL будет обходить свою собственную кучу, которая ничего не скажет о куче, используемой процессом службы.

Я думаю, что лучшим способом было бы написать свой собственный менеджер памяти (или купить), который предлагает эти данные. Любой другой способ изменил бы саму кучу и, таким образом, сделал бы недействительным результат.

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


Изменить: Я нашел ссылку на распределитель плиты (спасибо за комментарий), показывая статистику. Это на немецком языке, хотя и английская версия статьи не содержит так много информации. Используйте babelfish для перевода.

http://de.wikipedia.org/wiki/Slab_allocator( версия для бабушек)

http://www.usenix.org/event/usenix01/full_papers/bonwick/bonwick.pdf

Включение кучи с низкой фрагментацией для Windows может помочь выполнить работу на старых системах. в новых системах он включен по умолчанию (Vista, Server 2008)

  HANDLE heaps[1025];
  DWORD nheaps = GetProcessHeaps((sizeof(heaps) / sizeof(HANDLE)) - 1, heaps);
  for (DWORD i = 0; i < nheaps; ++i) {
    ULONG  enableLFH = 2;
    HeapSetInformation(heaps[i], HeapCompatibilityInformation, &enableLFH, sizeof(enableLFH));
  }

Существует инструмент VMMap от sysinternals (сейчас Microsoft), который дает хороший обзор фрагментации памяти.

Самый простой способ обнаружить фрагментацию - это определить наибольшее выделение, которое когда-либо будет делать ваша программа, а затем выделять, по крайней мере, вдвое больше этого количества время от времени. если распределение завершится неудачно, т.е. вернет NULL И ваше использование кучи, как определено кодом - что-то подобное в Windows

PROCESS_MEMORY_COUNTERS counters;
if(GetProcessMemoryInfo(process, &counters, sizeof(counters))){
    result = counters.WorkingSetSize;
}   

составляет менее некоторого процента системной памяти, обычно 75%, тогда у вас определенно есть проблема фрагментации.

Я согласен с Тобиасом - создание собственного менеджера памяти - отличный способ сделать это. Я знаю только несколько разработчиков, которым я бы доверял написать такой код...

Другая возможность состоит в том, чтобы время от времени выполнять собственную сортировку / консолидацию мусора на ваших объектах - при низких нагрузках... т.е. ваш сервис может быть неактивным какое-то время, пока он "дефрагментирует" используемую память, но я не уверен Вы можете гарантировать желаемое поведение без собственного управления памятью.

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

Во-первых, вы должны понять, какие распределения вы делаете. Я думаю, что простой способ сделать это - переопределить операторы new и delete, и из этих новых операторов вы должны посчитать некоторую статистику ваших распределений, а затем вызвать по умолчанию операторы new и delete вашего компилятора.

На мой взгляд, минимальной статистикой, которую вы должны учитывать, является количество распределений диапазонов общих размеров блоков.

например, блоки от 0 до 15 байтов, блоки от 16 до 32 байтов, блоки от 32 до 48 байтов, ...

Вы также можете добавить Количество последовательных распределений каждого размера блока

После сбора этих данных вы можете уменьшить проблему фрагментации, выровняв блоки по общим размерам.

Лучший и простой метод выравнивания - использовать блоки со степенью 2.

Например, чтобы выровнять число к ближайшему числу, которое делится на 16, вы можете использовать следующую функцию:

int align(int size)
{
    return ((size + 15) & ~0x0000000F);
}

Конечно, вы должны использовать свою статистику, чтобы выбрать лучшую степень 2, чтобы соответствовать. Цель состоит в том, чтобы достичь числа, которое большинство ваших выделений попадет в несколько диапазонов блоков, и в то же время сохранить разумные накладные расходы на выравнивание.

Удачи...

Другие вопросы по тегам