Может ли Win32 перемещать память, выделенную кучей?

У меня есть приложение .NET / native C ++. В настоящее время код C ++ выделяет память в куче по умолчанию, которая сохраняется в течение всего срока службы приложения. В основном функции / команды выполняются на C ++, что приводит к выделению / модификации текущей постоянной памяти. Я исследую способ отмены одной из этих функций / команд в середине выполнения. У нас есть сотни таких команд, и многие из них представляют собой очень сложный (устаревший) код.

Подход грубой силы, которого я пытаюсь избежать, заключается в изменении каждой команды / функции для проверки отмены и выполнения всей соответствующей очистки (освобождения памяти кучи). Я исследую многопоточный подход, в котором дополнительный поток получает запрос отмены и завершает поток выполнения команды. Я бы хотел, чтобы вся динамическая память была размещена в «частной куче» с использованием HeapCreate() (Win32). Таким образом, частная куча может быть уничтожена потоком, обрабатывающим запрос отмены. Однако, если команда выполняется до завершения, мне нужно, чтобы динамическая память сохранялась. В этом случае я хотел бы сделать логический эквивалент «перемещения» частной памяти кучи в кучу по умолчанию / кучу процесса без затрат на фактическую копию. Это вообще возможно? Имеет ли это вообще смысл?

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

Я использую 64-разрядную версию Windows 7 с 8 ГБ ОЗУ (считайте это целевой платформой). Приложение, с которым я работаю, составляет около 1 миллиона SLOC (половина C ++, половина C #). Я ищу любой опыт / предложения по управлению частной кучей или просто альтернативы моему решению.


person Community    schedule 19.01.2010    source источник
comment
Вы спрашиваете, можно ли переназначить память, которую вы выделяете в одной куче, другой куче без фактического копирования данных?   -  person John Dibling    schedule 19.01.2010
comment
Я на 92% уверен, что ответ отрицательный. Но недостаточно, чтобы опубликовать это в качестве ответа.   -  person John Dibling    schedule 19.01.2010
comment
Выделение памяти в куче - не единственная проблема, с которой вы столкнетесь с принудительным завершением потоков. Единственный безопасный способ завершить поток - изнутри. Таким образом, вы знаете, что поток не выполняет какую-то заблокированную операцию. Возможность прерывания должна быть неотъемлемой чертой ваших функций; это не то, что можно прикрутить снаружи.   -  person Rob Kennedy    schedule 20.01.2010


Ответы (4)


Куча - это своего рода большой кусок памяти. Это менеджер памяти на уровне пользователя. Куча создается вызовами системной памяти нижнего уровня (например, sbrk в Linux и VirtualAlloc в Windows). В куче вы можете запросить или вернуть небольшой кусок памяти с помощью malloc / new / free / delete. По умолчанию процесс имеет одну кучу (в отличие от стека, все потоки совместно используют кучу). Но у вас может быть много куч.

  • Можно ли объединить две кучи без копирования? Куча - это, по сути, структура данных, которая поддерживает список используемых и освобожденных фрагментов памяти. Итак, куча должна содержать своего рода бухгалтерские данные, называемые метаданными. Конечно, эти метаданные относятся к куче. AFAIK, никакой менеджер кучи не поддерживает операцию слияния двух куч. Я просмотрел весь исходный код реализации malloc в Linux glibc (реализация Дуга Ли ), но такой операции нет. Аналогичным образом реализованы и функции Windows Heap *. Таким образом, в настоящее время невозможно переместить или объединить две отдельные кучи.

  • Возможно ли иметь много куч? Я не думаю, что наличие большого количества куч должно быть большой проблемой. Как я уже говорил, куча - это просто структура данных, в которой хранятся используемые / освобожденные фрагменты памяти. Итак, должны быть некоторые накладные расходы. Но это не так уж и серьезно. Если вы посмотрите на одну из реализации malloc, то увидите malloc_state, базовая структура данных для каждой кучи. Например, вы можете создать другую кучу с помощью create_mspace (в Windows это HeapCreate), тогда вы получите новое состояние malloc. Он не такой уж и большой. Итак, если этот шаг (некоторые накладные расходы на кучу по сравнению с простотой реализации) в порядке, тогда вы можете продолжать.

Если бы я был на вашем месте, я бы попробовал, как вы описываете. Это имеет смысл для меня. Наличие большого количества объектов кучи не приведет к большим накладным расходам.

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

p.s. Ваша проблема кажется транзакцией, особенно программной транзакционной памятью. Типичная реализация буферов STM ожидает записи в память, а затем фиксируется в реальной системной памяти, если транзакция не имела конфликта.

person minjang    schedule 20.01.2010
comment
@STingRaySC: Сопоставление виртуального адреса и физического адреса не имеет ничего общего с этой проблемой. Мы просто не видим такого отображения. Мы рассматриваем только перемещение / слияние в виртуальном адресном пространстве. Программисты могут видеть только виртуальный адрес (за исключением некоторых встроенных или реальных ситуаций). Да, сопоставление с физическим адресом всегда можно изменить, но это полностью скрыто. Однако перемещение / объединение ч / б куч выполняется только в пределах виртуальных адресных пространств, поэтому, как правило, это невозможно, если мы не реализуем очень дорогостоящий механизм для отслеживания указателей, на которые имеются ссылки. - person minjang; 20.01.2010

Возможно, вам лучше использовать отдельные процессы вместо отдельных потоков:

  • использовать файлы с отображением памяти (то есть вообще не файл - просто перекрестно обрабатываемая разделяемая память)
  • убить процесс «чище», чем убить поток
  • Я думаю, что общая память может "пережить" убийство без движения - вы отображаете / отменяете отображение вместо перемещения

хотя вам, возможно, придется самостоятельно управлять памятью.

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

Просто идея!

person tony    schedule 19.01.2010

На странице Функции кучи в MSDN: «Выделенная память by HeapAlloc не может быть перемещен. Адрес, возвращаемый HeapAlloc, действителен до тех пор, пока блок памяти не будет освобожден или перераспределен; блок памяти не нужно блокировать ».

Можете ли вы повторно связать устаревшие приложения с вашей собственной реализацией malloc ()? Если это так, вы сможете обойтись без изменения остальной части кода. Ваша настраиваемая библиотека malloc может отслеживать выделенные блоки по потокам и иметь функцию FreeAllByThreadId (), которую вы вызываете после уничтожения потока унаследованной функции. Вы можете использовать частные кучи внутри библиотеки.

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

person AShelly    schedule 19.01.2010

Нет. Память нельзя перемещать между кучами.

person John Knoeller    schedule 19.01.2010