Как решить проблему фрагментации памяти

Иногда у нас возникают проблемы, из-за которых наши длительные серверные процессы (работающие на Windows Server 2003) выдают исключение из-за сбоя выделения памяти. Мы подозреваем, что эти выделения не выполняются из-за фрагментации памяти.

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

1) Используйте Windows Куча с низким уровнем фрагментации

2) jemalloc - используется в Firefox 3

3) маллок Дуга Ли

Наш серверный процесс разработан с использованием кроссплатформенного кода C++, поэтому в идеале любое решение также должно быть кроссплатформенным (не страдают ли операционные системы * nix от такого типа фрагментации памяти?).

Кроме того, правильно ли я думаю, что LFH теперь является механизмом распределения памяти по умолчанию для Windows Server 2008 / Vista? ... «Уйдут» ли мои текущие проблемы, если наши клиенты просто обновят ОС своих серверов?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
49
0
47 019
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

Я бы заподозрил утечку раньше, чем заподозрил бы фрагментацию.

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

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

Вы можете уменьшить фрагментацию, уменьшив объем освобождаемого выделения.

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

Вы можете использовать _CrtDumpMemoryLeaks (); для сброса утечек памяти в окно отладки при запуске отладочной сборки, однако я считаю, что это характерно для компилятора Visual C. (это в crtdbg.h)

@nsaners - Я почти уверен, что проблема в фрагментации памяти. Мы проанализировали минидампы, которые указывают на проблему, когда выделяется большой (5-10 МБ) фрагмент памяти. Мы также наблюдали за процессом (на месте и в разработке), чтобы проверить наличие утечек памяти - никаких утечек обнаружено не было (объем памяти, как правило, довольно низкий).

Вы должны быть осторожны со своим заключением здесь. Даже очень небольшая утечка может вызвать массивную фрагментацию кучи. Подумайте о каждой 1-байтовой утечке malloc (), которая не освобождена, как о пулевом отверстии в куче. Соседние блоки никогда не могут объединиться.

Tall Jeff 14.09.2008 06:28

Проблема действительно возникает в Unix, хотя обычно она не так серьезна.

Куча с низким уровнем фрагментации помогла нам, но мои коллеги клянутся Умная куча (в течение многих лет он использовался кросс-платформенным в нескольких наших продуктах). К сожалению, в этот раз по другим причинам мы не смогли использовать Smart Heap.

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

Как обычно, вы можете потратить впустую память, чтобы набрать скорость.

Этот метод бесполезен для распределителя общего назначения, но он имеет свое место.

По сути, идея состоит в том, чтобы написать распределитель, который возвращает память из пула, где все выделения имеют одинаковый размер. Этот пул никогда не может стать фрагментированным, потому что любой блок ничем не хуже другого. Вы можете уменьшить потери памяти, создав несколько пулов с фрагментами разного размера и выбрав пул с наименьшим размером фрагмента, который по-прежнему превышает запрошенное количество. Я использовал эту идею для создания распределителей, работающих в O (1).

Я не согласен с этой идеей. Увеличение размера всех выделений создает ВНУТРЕННЮЮ фрагментацию. Если вы можете тратить память впустую, лучше просто увеличить общий размер кучи, что само по себе может предотвратить превращение фрагментации в проблему. т.е.: полностью исключена нестабильность кучи.

Tall Jeff 14.09.2008 06:33
Ответ принят как подходящий

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

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

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

Итак, как только вы исключили утечку, вы можете подумать об использовании лучшего распределителя. Маллок Дуга Ли, предложенный в вопросе, является очень хорошим распределителем для приложений общего пользования и очень надежным наиболее того времени. Другими словами, это было проверено временем, чтобы работать очень хорошо для большинства приложений. Однако ни один алгоритм не является идеальным для приложений все, и любой подход к алгоритму управления может быть нарушен правильными патологическими условиями, противоречащими его конструкции.

Почему у вас проблема фрагментации? - Источниками проблем фрагментации являются вызванный из-за поведения приложения и они связаны с сильно различающимся временем жизни выделения в одной и той же области памяти. То есть некоторые объекты выделяются и освобождаются регулярно, в то время как другие типы объектов сохраняются в течение длительных периодов времени в одной и той же куче ... думайте об объектах с более длительным сроком службы как о прокладывании дыр в более крупных областях арены и тем самым предотвращении объединение соседних блоков, которые были освобождены.

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

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

Кроме того, с хорошим распределителем кучи, таким как Doug Lea, делать размеры блоков более похожими не нужно, потому что распределитель уже будет выполнять схему сегментирования мощности двух размеров, что сделает совершенно ненужным искусственно настраивать размеры распределения, передаваемые в malloc ( ) - по сути, его менеджер кучи делает это за вас автоматически гораздо более надежно, чем приложение сможет вносить изменения.

Мне нравится идея иметь как временную, так и постоянную кучу. Это похоже на концепцию «генерации» в современных сборщиках мусора. Широко ли реализована эта идея в схемах выделения памяти вручную?

Waylon Flinn 17.04.2009 03:27

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

Tall Jeff 26.04.2009 00:44

Как вы предполагаете, malloc Дуга Ли может сработать. Это кроссплатформенный код, который использовался в коде доставки. По крайней мере, его должно быть легко интегрировать в ваш код для тестирования.

Я проработал несколько лет в средах с фиксированной памятью, и эта ситуация, безусловно, является проблемой даже в нефиксированных средах. Мы обнаружили, что распределители CRT, как правило, довольно плохо воняют с точки зрения производительности (скорости, эффективности потраченного впустую пространства и т. д.). Я твердо уверен, что если вам очень нужен хороший распределитель памяти в течение длительного периода времени, вы должны написать свой собственный (или посмотреть, подойдет ли что-то вроде dlmalloc). Хитрость заключается в том, чтобы написать что-то, что работает с вашими шаблонами распределения, и это больше связано с эффективностью управления памятью, как почти все остальное.

Попробуйте dlmalloc. Я определенно оцениваю это как должное. Он также довольно настраиваемый, поэтому вы можете повысить эффективность, изменив некоторые параметры времени компиляции.

Честно говоря, не стоит рассчитывать на то, что что-то «уходит» с новыми реализациями ОС. Пакет обновления, патч или другая новая ОС через N лет могут усугубить проблему. Опять же, для приложений, которым требуется надежный диспетчер памяти, не используйте стандартные версии, доступные с вашим компилятором. Найдите тот, который подходит для ситуации ваш. Начните с dlmalloc и настройте его, чтобы увидеть, сможете ли вы добиться поведения, которое лучше всего подходит для вашей ситуации.

Я думаю, вы слишком рано исключили утечку памяти. Даже крошечная утечка памяти может вызвать серьезную фрагментацию памяти.

Предположим, ваше приложение ведет себя следующим образом:
Выделить 10MB
Выделить 1 байт
Бесплатно 10MB
(ой, мы не освободили 1 байт, но кого волнует 1 крошечный байт)

Это похоже на очень маленькую утечку, вы вряд ли заметите это при мониторинге только общего объема выделенной памяти. Но эта утечка в конечном итоге приведет к тому, что память вашего приложения будет выглядеть так:
.
.
Бесплатно - 10MB
.
.
[Выделен -1 байт]
.
.
Бесплатно - 10MB
.
.
[Выделен -1 байт]
.
.
Бесплатно - 10MB
.
.

Эта утечка не будет замечена ... пока вы не выделите 11MB
Предполагая, что в ваши минидампы включена полная информация о памяти, я рекомендую использовать DebugDiag для обнаружения возможных утечек. В сгенерированном отчете о памяти внимательно изучите количество выделенных (не размер).

если про Win32 - можно попробовать что-нибудь выжать с помощью LARGEADDRESSAWARE. У вас будет ~ 1 ГБ дополнительной дефрагментированной памяти, поэтому ваше приложение будет фрагментировать ее дольше.

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

Ваша память и скорость могут немного пострадать (подкачка), но быстрое оборудование и большая оперативная память могут помочь.

Это была старая уловка UNIX с демонами, когда потоков еще не существовало.

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