No Image

Что такое фрагментация памяти

СОДЕРЖАНИЕ
0 просмотров
22 января 2020

Я слышал, что термин «фрагментация памяти» несколько раз использовался в контексте динамического выделения памяти в C ++. Я нашел несколько вопросов о том, как бороться с фрагментацией памяти, но не могу найти прямой вопрос, который касается самой этой проблемы. Так:

  • Что такое фрагментация памяти?
  • Как я могу определить, является ли фрагментация памяти проблемой для моего приложения? Какая программа больше всего страдает?
  • Каковы хорошие распространенные способы борьбы с фрагментацией памяти?
  • Я слышал, что использование динамических распределений может увеличить фрагментацию памяти. Это правда? Я понимаю, что в контексте C ++ все стандартные контейнеры (std :: string, std :: vector и т. Д.) Используют динамическое распределение памяти. Если они используются в программе (особенно в std :: string), является ли проблема фрагментации памяти более вероятной?
  • Как можно бороться с фрагментацией памяти в приложениях с интенсивным использованием STL?

Представьте, что у вас есть «большое» (32 байта) пространство свободной памяти:

Теперь выделим некоторые из них (5 выделений):

Теперь освободите первые четыре распределения, но не пятое:

Теперь попробуйте выделить 16 байтов. К сожалению, я не могу, хотя почти вдвое больше свободных.

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

тогда как виртуальная память (будучи намного больше) может выглядеть так:

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

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

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

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

Фрагментация памяти – это когда большая часть вашей памяти выделяется в большом количестве несмежных блоков или блоков – оставляя значительный процент вашей общей памяти нераспределенной, но непригодной для большинства типичных сценариев. Это приводит к исключениям нехватки памяти или ошибкам выделения (т. Е. Malloc возвращает ноль).

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

Теперь представьте, что стена – это ваша (куча) памяти, а картинки – это объекты. Это фрагментация памяти.

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

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

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

Каковы хорошие распространенные способы борьбы с фрагментацией памяти?

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

Фрагментация памяти – это та же концепция, что и фрагментация диска: она относится к неиспользуемому пространству, потому что используемые области не упакованы достаточно тесно.

Предположим для простого примера игрушки, что у вас есть десять байтов памяти:

Теперь давайте выделим три трехбайтовых блока с именами A, B и C:

Теперь освободите блок B:

Что произойдет, если мы попытаемся выделить четырехбайтовый блок D? Ну, у нас есть четыре байта свободной памяти, но у нас нет четырех смежных свободных байтов памяти, поэтому мы не можем выделить D! Это неэффективное использование памяти, потому что мы должны были хранить D, но мы не смогли. И мы не можем переместить C, чтобы освободить место, потому что очень вероятно, что некоторые переменные в нашей программе указывают на C, и мы не можем автоматически найти и изменить все эти значения.

Откуда ты знаешь, что это проблема? Ну, самый большой признак в том, что размер виртуальной памяти вашей программы значительно превышает объем памяти, который вы фактически используете. В реальном примере у вас будет гораздо больше, чем десять байтов памяти, поэтому D будет просто выделяться, начиная с байта 9, а байты 3-5 останутся неиспользованными, если вы позже не выделите что-то длиной три байта или меньше.

В этом примере 3 байта – это не пустая трата, но рассмотрим более патологический случай, когда два выделения по пару байтов, например, на расстоянии десять мегабайт в памяти, и вам нужно выделить блок размером 10 мегабайт. +1 байт. Для этого вам нужно попросить у ОС больше, чем на десять мегабайт, виртуальной памяти, даже если вам не хватает места на один байт.

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

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

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

Читайте также:  Топ недорогих портативных колонок

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

  • Как я могу определить, является ли фрагментация памяти проблемой для моего приложения? Какая программа больше всего страдает?

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

  • Каковы хорошие распространенные способы борьбы с фрагментацией памяти?

Используйте хороший распределитель памяти. IIRC, те, которые используют стратегию «наилучшего соответствия», обычно намного лучше избегают фрагментации, хотя и немного медленнее. Однако было также показано, что для любой стратегии распределения существуют наихудшие патологические случаи. К счастью, типичные схемы распределения большинства приложений на самом деле относительно благоприятны для обработки распределителями. Там есть куча статей, если вам интересны детали:

  • Пол Р. Уилсон, Марк С. Джонстон, Майкл Нили и Дэвид Болес. Динамическое распределение памяти: обзор и критический обзор. В материалах международного семинара 1995 года по управлению памятью, Springer Verlag LNCS, 1995
  • Марк С. Джонстон, Пол Р. Уилсон. Проблема фрагментации памяти: решена? В уведомлениях ACM SIG-PLAN, том 34 № 3, страницы 26-36, 1999
  • MR Garey, RL Graham и JD Ullman. Наихудший анализ алгоритмов выделения памяти. В четвертом ежегодном симпозиуме ACM по теории вычислений, 1972

Обновить:
Google TCMalloc: Malloc с кэшированием потоков
Было обнаружено, что он довольно хорошо справляется с фрагментацией в длительном процессе.

Я занимаюсь разработкой серверного приложения, в котором были проблемы с фрагментацией памяти в HP-UX 11.23 / 11.31 ia64.

Это выглядело так. Был процесс, который делал распределение памяти и освобождение и работал в течение нескольких дней. И хотя утечек памяти не было, потребление памяти процессом продолжало расти.

О моем опыте. В HP-UX очень легко обнаружить фрагментацию памяти с помощью HP-UX GDB. Вы устанавливаете точку останова, и когда вы нажимаете на нее, вы запускаете эту команду: info heap и видите все выделения памяти для процесса и общий размер кучи. Затем продолжите свою программу, а через некоторое время снова достигните точки останова. Вы снова делаете info heap . Если общий размер кучи больше, но количество и размер отдельных выделений одинаковы, вероятно, у вас есть проблемы с выделением памяти. При необходимости сделайте эту проверку несколько раз.

Мой способ улучшения ситуации заключался в следующем. После того, как я провел некоторый анализ с HP-UX GDB, я увидел, что проблемы с памятью были вызваны тем, что я использовал std::vector для хранения некоторых типов информации из базы данных. std::vector требует, чтобы его данные хранились в одном блоке. У меня было несколько контейнеров на основе std::vector . Эти контейнеры были регулярно воссозданы. Часто возникали ситуации, когда новые записи добавлялись в базу данных, а затем контейнеры создавались заново. А так как воссозданные контейнеры были больше, они не помещались в доступные блоки свободной памяти, и среда выполнения запросила новый больший блок от ОС. В результате, несмотря на отсутствие утечек памяти, потребление памяти процессом возросло. Я улучшил ситуацию, когда менял контейнеры. Вместо std::vector я начал использовать std::deque который имеет другой способ выделения памяти для данных.

Я знаю, что одним из способов избежать фрагментации памяти в HP-UX является использование Small Block Allocator или MallocNextGen. В RedHat Linux распределитель по умолчанию, кажется, довольно хорошо справляется с распределением множества маленьких блоков. В Windows имеется Low-fragmentation Heap и она решает проблему большого количества небольших выделений.

Насколько я понимаю, в приложении, интенсивно использующем STL, вы должны сначала выявить проблемы. Распределители памяти (как в libc) на самом деле решают проблему большого количества небольших выделений, что типично для std::string (например, в моем серверном приложении есть много строк STL, но, как я вижу из info heap они не являются вызывая любые проблемы). У меня сложилось впечатление, что вам нужно избегать частых крупных выделений. К сожалению, бывают ситуации, когда вы не можете избежать их и должны изменить свой код. Как я сказал в моем случае, я улучшил ситуацию, когда переключился на std::deque . Если вы обнаружите фрагментацию вашей памяти, возможно, можно будет поговорить об этом более точно.

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

Появление или непоявление фрагментации зависит от метода организации памяти.

Внутренняя фрагментация – при заполнении страниц в среднем половина последней страницы остаётся незаполненной. Эти «дыры» и есть внутренняя фрагментация.

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

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

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

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

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: При сдаче лабораторной работы, студент делает вид, что все знает; преподаватель делает вид, что верит ему. 9530 – | 7348 – или читать все.

91.146.8.87 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Я слышал термин "фрагментация памяти", используемый несколько раз в контексте динамического выделения памяти C++. Я нашел несколько вопросов о том, как бороться с фрагментацией памяти, но не могу найти прямого вопроса, который имеет дело с самим собой. Итак:

  • что такое фрагментация памяти?
  • как я могу сказать, является ли фрагментация памяти проблемой для моего приложения? Какая программа, скорее всего, пострадает?
  • что хорошие общие пути к иметь дело с фрагментацией памяти?
  • Я слышал, что использование динамических распределений может увеличить фрагментацию памяти. Это правда? В контексте C++ я понимаю, что все стандартные контейнеры (std::string, std::vector и т. д.) используют динамическое выделение памяти. Если они используются во всей программе (особенно std::string), является ли фрагментация памяти более вероятной проблемой?
  • как можно справиться с фрагментацией памяти в STL-тяжелое применение?

11 ответов

представьте, что у вас есть "большой" (32 байта) объем свободной памяти:

теперь выделите некоторые из них (5 выделений):

теперь освободите первые четыре распределения, но не Пятый:

теперь попробуйте выделить 16 байт. Ой, я не могу, хотя там почти вдвое больше свободного.

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

Читайте также:  Электронный контроллер стиральной машины

в то время как виртуальная память (гораздо больше) может выглядеть так:

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

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

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

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

Что такое фрагментация памяти?

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

самый простой способ думать об этом: представьте, у вас есть большая пустая стена что вам нужно поставить фотографии различных размеров on. Каждый снимок занимает определенный размер, и вы, очевидно, не можете разделить его на более мелкие кусочки, чтобы он подошел. Вам нужно пустое место на стене, размер картины, или вы не можете поставить его. Теперь, если вы начнете вешать картины на стену, и вы не будете осторожны в том, как вы их расположите, вы скоро получите стену, которая частично покрыта фотографиями, и даже если у вас могут быть пустые места, большинство новых фотографий не будут подходят, потому что они больше, чем свободных мест. Вы все еще можете повесить очень маленькие фотографии, но большинство из них не подходят. Таким образом, вам придется переставить (компактные) те, которые уже на стене, чтобы освободить место для большего..

теперь представьте, что стена – это ваша (куча) память, а изображения-объекты.. Это фрагментация памяти..

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

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

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

каковы хорошие общие способы борьбы с фрагментацией памяти?

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

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

предположим для простого примера игрушки, что у вас есть десять байтов памяти:

теперь выделим три трехбайтовых блока, назовем A, B и C:

Теперь освободите блок B:

Теперь, что произойдет, если мы попытаемся выделить четыре байта в блоке D? Ну, у нас есть четыре байт свободной памяти, но у нас нет четыре!–12–>прилежащей байты памяти свободны, поэтому мы не можем выделить D! Это неэффективное использование памяти, потому что мы должны были иметь возможность хранить D, но мы не смогли. И мы не можем переместить C, чтобы освободить место, потому что очень вероятно, что некоторые переменные в нашей программе указывают на C, и мы не можем автоматически найти и изменить все эти значения.

откуда вы знаете, что это проблема? Ну, самый большой признак, что ваша программа виртуальная размер памяти значительно больше, чем объем памяти, который вы фактически используете. В реальном примере у вас будет намного больше десяти байтов памяти, поэтому D будет просто выделен, начиная с байта 9, а байты 3-5 останутся неиспользуемыми, если позже вы не Выделите что-то длиной три байта или меньше.

в этом примере 3 байта не так много, чтобы тратить, но рассмотрим более патологический случай, когда два выделения пары байтов, например, десять мегабайт отдельно в памяти и нужно выделить блок размером 10 мегабайт + 1 байт. Для этого вам нужно попросить у ОС на десять мегабайт больше виртуальной памяти, хотя вам всего на один байт не хватает места.

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

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

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

фрагментация памяти-это проблема непригодности памяти, даже если она теоретически доступна. Существует два вида фрагментации: внутренней фрагментации – это память, которая выделяется, но не может использоваться (например, когда память выделяется в 8 байтовых кусках, но программа неоднократно делает одиночные алляции, когда ей нужно всего 4 байта). внешняя фрагментация is проблема свободной памяти разделяется на множество небольших фрагментов, так что большие запросы на выделение не могут быть выполнены, хотя есть достаточно общей свободной памяти.

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

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

  • каковы хорошие общие способы борьбы с фрагментацией памяти?

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

  • Пол Р. Уилсон, Марк С. Джонстон, Майкл Нили и Дэвид Боулз. Динамическое распределение хранилища: обзор и критический обзор. В работе 1995 года Международный семинар по управлению памятью, Springer Verlag LNCS, 1995
  • Марк С. Джонстон, Пол Р. Уилсон. Проблема Фрагментации Памяти: Решена? В ACM Sig-PLAN Notices, том 34 № 3, стр. 26-36, 1999
  • м. р. Гэри, Л. Р. Грэхэма и Дж. д. Ульман. Наихудший анализ алгоритмов выделения памяти. На четвертом ежегодном симпозиуме ACM по теории вычислений, 1972
Читайте также:  Срок службы удлинителя электрического

обновление:
Google TCMalloc: Кэширование Потоков Malloc
Было обнаружено, что это довольно хорошо при обработке фрагментации в длительном процессе.

Я разрабатывал серверное приложение, у которого были проблемы с фрагментацией памяти на HP-UX 11.23/11.31 ia64.

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

о моем опыте. На HP-UX очень легко найти фрагментацию памяти с помощью HP-UX gdb. Вы устанавливаете точку останова, и когда вы нажимаете ее, вы запускаете эту команду: info heap и просмотреть все выделения памяти для процесса и общий размер кучи. Затем вы продолжаете свою программу, а затем через некоторое время снова попадаете в точку останова. Ты снова info heap . Если общий размер кучи больше, но количество и размер отдельных выделений одинаковы, тогда, вероятно, у вас есть проблемы с выделением памяти. При необходимости сделайте эту проверку несколько раз.

мой способ улучшить ситуацию был таков. После того, как я сделал некоторый анализ с HP-UX gdb, я увидел, что проблемы с памятью были вызваны тем, что я использовал std::vector для хранения некоторых видов информации из базы данных. std::vector требует, чтобы его данные хранились в одном блоке. У меня было несколько контейнеры на основе std::vector . Эти контейнеры регулярно воссоздавались. Часто возникали ситуации, когда в базу данных добавлялись новые записи, после чего контейнеры воссоздавались. И поскольку воссозданные контейнеры были больше, они не вписывались в доступные блоки свободной памяти, и среда выполнения попросила новый больший блок из ОС. В результате, несмотря на отсутствие утечек памяти, потребление памяти в процессе росло. Я улучшил ситуацию, когда изменил стеклотара. Вместо std::vector Я начал использовать std::deque , который имеет другой способ выделения памяти для данных.

Я знаю, что один из способов избежать фрагментации памяти на HP-UX-использовать либо небольшой блок-распределитель, либо использовать MallocNextGen. В RedHat Linux распределитель по умолчанию, похоже, довольно хорошо справляется с выделением большого количества небольших блоков. В Windows есть Low-fragmentation Heap и он адресует проблему большого количества небольших распределений.

насколько я понимаю, в приложении STL-heavy вы должны сначала определить проблемы. Распределители памяти (как в libc) фактически обрабатывают проблему большого количества небольших распределений, что характерно для std::string (например, в моем серверном приложении есть много строк STL, но, как я вижу, от запуска info heap Они не вызывают никаких проблем). У меня сложилось впечатление, что вам нужно избегать частых больших ассигнований. К сожалению, есть ситуации, когда вы не можете избежать их и должны изменить свой код. Как я сказал в мой случай я улучшил ситуацию, когда переключился на std::deque . Если вы определите ваши fragmention памяти можно говорить о нем более точно.

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

Теперь, когда obj2 освобождается, у вас есть 120kb неиспользуемой памяти, но вы не можете выделить полный блок 120kb, потому что память фрагментирована.

общие методы, чтобы избежать этого эффекта включают кольцевые буферы и объект, бассейны. В контекст STL, такие методы, как std::vector::reserve() могу помочь.

очень подробный ответ на фрагментацию памяти можно найти здесь.

это кульминация 11 лет фрагментации памяти ответы, которые я предоставлял людям, задававшим мне вопросы о фрагментации памяти в softwareverify.com

когда ваше приложение использует динамическую память, оно выделяет и освобождает куски памяти. В начале все пространство памяти вашего приложения представляет собой один непрерывный блок свободной памяти. Однако, когда вы выделяете и освобождаете блоки разного размера, память начинает получать фрагментированный, т. е. вместо большого смежного свободного блока и ряда смежных выделенных блоков будет смешанный выделенный и свободный блоки вверх. Поскольку свободные блоки имеют ограниченный размер, их трудно повторно использовать. Е. Г. вы можете иметь 1000 байт свободной памяти, но не могу выделить память для 100 байт блока, потому что все свободные блоки не более 50 байт.

другой, неизбежны, но менее проблематичный источник фрагментации заключается в том, что в большинстве архитектур, адреса памяти должны быть соответствие до 2, 4, 8 etc. байтовые границы (т. е. адреса должны быть кратны 2, 4, 8 и т. д.) Это означает, что даже если у вас есть, например, структура, содержащая 3 char поля, ваша структура может иметь размер 12 вместо 3, из-за того, что все поля выровнены по 4-байтовой границе.

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

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

по-видимому, нет хорошего портативного способа обнаружения фрагментации памяти в C++ приложения. См.ответ для получения более подробной информации.

каковы хорошие общие способы борьбы с фрагментацией памяти?

это сложно в C++, так как вы используете прямые адреса памяти в указателях, и у вас нет контроля над тем, кто ссылается на конкретный адрес памяти. Поэтому перестановка выделенных блоков памяти (как это делает сборщик мусора Java) не является опцией.

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

Это супер-упрощенная версия для чайников.

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

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

Это то, что называется фрагментацией.

когда вы хотите добавить элемент в кучу, происходит то, что компьютер должен выполнить поиск места для этого элемента. Вот почему динамические распределения, когда они не выполняются в пуле памяти или с объединенным распределителем, могут "замедлять" работу. Для тяжелого приложения STL, если вы делаете многопоточность, есть клад распределитель или TBB Intel версия.

теперь, когда память фрагментирована, могут произойти две вещи:

  1. там придется больше поисков, чтобы найти хорошее место для палки "больших" объектов. То есть, когда вокруг разбросано много мелких предметов, найти хороший смежный кусок памяти при определенных условиях может быть трудно (это экстремально.)
  2. памяти не некоторые легко читать лица. Процессоры ограничены тем, сколько они могут удерживать и где. Они делают это путем замены страниц, если элемент, который им нужен, находится в одном месте, а текущие адреса-в другом. Если вам постоянно приходится менять местами страницы, обработка может замедлиться (опять же, экстремальные сценарии, где это влияет на производительность.) См. эту публикацию на виртуальный.

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

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

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

Что вы должны помнить, так это то, что на 32-битном x86 настольная система, у вас есть весь 2GB памяти, который разделен на 4KB "страницы" (довольно уверен, что размер страницы одинаковый на всех системах x86). Вам придется вызвать некоторую фрагментацию omgwtfbbq, чтобы иметь проблему. Фрагментация действительно является проблемой прошлого, так как современные кучи чрезмерно велики для подавляющего большинства приложений, и есть распространенность систем, которые способны противостоять этому, таких как управляемые кучи.

Комментировать
0 просмотров
Комментариев нет, будьте первым кто его оставит

Это интересно
Adblock detector