Есть ли смысл перегружать глобальные new и delete?

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

Добавление Я только что обнаружил ошибку в перегруженной функции delete - память освобождалась не всегда. И это было в не очень критичном к памяти приложении. Кроме того, отключение этих перегрузок снижает производительность всего на ~0.5%.

Решение

Мы перегрузить глобальные операторы new и delete, где я работаю по многим причинам:

  • зачет все небольшие выделения -- уменьшает накладные расходы, уменьшается фрагментация, может повысить производительность для малых-к alloc-тяжелые приложения
  • обрешетка распределения с известной жизни-игнорировать все освобождает до самого конца этого периода, затем все они вместе (правда мы делаем это с местного оператора перегрузки, чем глобальные)
  • трассы регулировка ... чтобы границы строки кэша, и т. д
  • к alloc заполнить -- помогая разоблачать использование неинициализированных переменных
  • бесплатно заполнить -- помогая разоблачать использование ранее удаленной памяти
  • задержано бесплатно -- повышение эффективности свободной заливки, иногда увеличивая производительность
  • стражи или fenceposts -- помогая разоблачать переполнения буфера, недогрузки, а иногда дикий указатель
  • перенаправления отчисления -- учет Нума специальных областей памяти, или даже держать его отдельных систем, отдельных в памяти (например, встроенных скриптовых языков или DSL-языки)
  • вывоз мусора или очистки -- опять же полезно для тех, встраиваемых скриптовых языков
  • кучи проверки - вы можете пройтись по структуре кучи данных каждые N allocs/освобождает, чтобы убедиться, что все выглядит ОК
  • бухгалтерии, включая утечка отслеживания и использование стоп-кадров/статистика (стеки, распределения возрастов и т. д.)

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

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

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

Взгляните на некоторые из распределителей и систем отладки, там для C/C++ и вы'Лл быстро придумать эти и другие идеи:

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

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

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

Комментарии (3)

Наиболее распространенной причиной перегрузки new и delete является проверка на утечки памяти и статистика использования памяти. Заметим, что понятие "утечка памяти" обычно обобщается до ошибок памяти. Вы можете проверить такие вещи, как двойное удаление и выход за пределы буфера.

После этого обычно используются схемы распределения памяти, такие как garbage collection и pooling.

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

Комментарии (0)

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

Например, вы можете иметь ряд пулов памяти с фиксированными размерами блока. Переопределение глобального нового позволяет направить все 61-байтовое, скажем, бассейн с 64-байтных блоков, все 768-1024 байт allocs в 1024b-блок, бассейн, все, кто выше, что к 2048 Byte блока в бассейн, и ничего больше 8кб Генеральной рваные кучи.

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

Это делается часто в Систем, время и пространство-важнейшие, такие как игры. 280Z28, Meeh, и Дэн Олсон описал почему.

Комментарии (1)

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

Edit: Для своего собственного кода я бы делал это только в крайнем случае. И под этим я подразумеваю, что я почти положительно никогда бы не использовал его. Но мои личные проекты, очевидно, гораздо меньше/имеют совсем другие требования.

Комментарии (1)

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

Комментарии (0)

Перегружать новый & удаление позволяет добавить тег к вашему выделения памяти. Я тег выделения в системе или управления или промежуточного. Я могу посмотреть, во время выполнения, сколько каждый использует. Может быть, я хочу видеть использование парсера отделена от пользовательского интерфейса или сколько кусок промежуточного действительно работает!

Вы также можете использовать его, чтобы положить защитной полосы вокруг выделенной памяти. Если ваше приложение падает, можно посмотреть по адресу. Если вы видите содержимое как "0xABCDABCD и" (или что бы вы ни выбрали в качестве охранника) вы обращаетесь к памяти вы не'т себе.

Возможно, после вызова "удалить", вы можете заполнить это пространство с такой же узнаваемый узор. Я считаю, что VisualStudio делает что-то подобное в отладке. Не'т заполнить это неинициализированной памяти с 0xCDCDCDCD?

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

Комментарии (0)

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

Например, в программировании ядра, по умолчанию new и delete Дон't работа, так как они полагаются на библиотеки в пользовательском режиме выделить память.

Комментарии (0)

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

Но 99% времени это'ы сделали как функция отладки в журнал, как часто, где, когда память выделяется и освобождается.

Комментарии (1)

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

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

Конечно, не говорю, что это хорошо использовать, но это, наверное, один из наиболее творческих из них там...

* , к сожалению, это было'т так много о фактическом безопасность как вид безопасности...

Комментарии (4)

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

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

Комментарии (0)

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

На Linux, вы можете поместить свои собственные версии malloc в месте системы, как, например, здесь:

http://developers.sun.com/solaris/articles/lib_interposers.html

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

Поскольку вы делаете это в общей библиотеке с LD_PRELOAD, вы Дон'т даже нужно перекомпилировать приложение.

Комментарии (2)

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

Самый простой способ-это забронировать пустой блок памяти при запуске для этой цели. Вы также можете иметь некоторые кэш можно задействовать - идея та же.

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

Комментарии (0)

Photoshop Плагины, написанные на C++, должны переопределить `оператор new, чтобы они получить памяти через фотошоп.

Комментарии (0)

Самый распространенный случай использования это, наверное, проверка утечка.

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

Комментарии (1)

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

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

Комментарии (0)

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

Комментарии (0)