Подтвердить что ты не робот

Требуется ли время для освобождения памяти?

У меня есть программа на С++, которая во время выполнения выделяет около 3-8 ГБ памяти для хранения хеш-таблицы (я использую tr1/unordered_map) и других других структур данных.

Однако, в конце выполнения, перед возвратом в оболочку будет долгая пауза.

Например, в самом конце моей основной функции у меня есть

std::cout << "End of execution" << endl;

Но выполнение моей программы будет похоже на

$./program
делать вещи...
Конец исполнения
[длительная пауза, возможно, 2 минуты]
$ - возвращает в оболочку

Является ли это ожидаемым поведением или я делаю что-то неправильно?

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

Просьба сообщить:)

Изменить: самая большая структура данных - это unordered_map с ключом string и сохраняет list integers.

Я использую g++ -O2 в linux, компьютер, который я использую, имеет 128 ГБ памяти (с большей частью этого бесплатно). Есть несколько гигантских объектов

Решение: я в конечном итоге избавился от хеш-таблицы, так как в любом случае он был почти полным. Это решило мою проблему.

4b9b3361

Ответ 1

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

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

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

Вы сделали бы это, непосредственно вызвав функцию libc exit(3) или операционную систему _exit(2). Тем не менее, я был бы очень осторожен в проверке этого, не сокращал бы любые другие (важные) очистки, некоторые С++ деструктор код может делать. И то, что это делает или не делает, сильно зависит от системы (операционная система, компилятор, libc, используемые вами API,...).

Ответ 2

Да, освобождение памяти может занять некоторое время, а также, возможно, вы выполняете код, как вызываемые деструкторы. Photoshop не использует память 3-8 ГБ.

Также вы должны добавить профилирование в свое приложение, чтобы подтвердить, что это освобождение памяти, а не что-то еще.

Ответ 3

(я начал это как ответ ndim, но он дошел до конца)

Поскольку ndim уже отправлен, окончание может занять много времени.
Возможные причины:

  • у вас много распределений, а части кучи меняются на диск.
  • длинные деструкторы
  • другие atexit подпрограммы
  • Особая очистка ОС, такая как уведомление DLL о потоке и завершение процесса в Windows (не знаю, что именно происходит в Linux.)

exit не является наихудшим обходным решением здесь, однако фактическое поведение зависит от системы. например exit на WIndows/MSVC CRT будет запускаться глобальные деструкторы /atexit подпрограммы, а затем вызывать ExitProcess, который закрывает дескрипторы (но не обязательно сбрасывает их - по крайней мере, это не гарантируется).

Недостатки: Деструкторы выделенных кучей объектов не запускаются - если вы полагаетесь на них (например, чтобы сохранить состояние), вы являетесь тостами. Кроме того, отслеживание реальных утечек памяти становится намного сложнее.

Найти причину. Сначала вы должны проанализировать, что происходит.

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

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

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

Ответ 4

Конечно, это возможно.

Около 7 лет назад у меня была аналогичная проблема в проекте, было гораздо меньше памяти, но компьютеры были медленнее, я полагаю.

В конце концов, нам пришлось смотреть бесплатно на сборку, чтобы понять, почему она была настолько медленной, и казалось, что она по существу сохранила освобожденные блоки в связанном списке, чтобы их можно было перераспределить, а также сканирование этого списка ищет блоки для объединения. Сканирование списка было операцией O (n), но освобождение объектов "n" превратило их в O (n ^ 2)

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

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

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

Ответ 5

Я не могу себе представить, как бы вы использовали достаточное количество памяти для этого, но одним способом я ускорил программу: boost::object_pool, чтобы выделить память для двоичного дерева. Главным преимуществом для меня было то, что я мог просто положить пул объектов в качестве переменной-члена дерева, а когда дерево исчезло из области видимости или было удалено, пул объектов был бы удален сразу (позволяя мне не использовать рекурсивный деконструктор для узлов). object_pool действительно вызывает все деконтерструкторы своих объектов при выходе. Я не уверен, что он обрабатывает пустые деконтракторы особым способом или нет.

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

Ответ 6

Свободная память может занять время - обновляются структуры данных. Сколько времени зависит от используемого распределителя.

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

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

Ответ 7

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

Также рассмотрим этот другой вопрос: С++ STL-совместимые распределители

Ответ 8

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

Ответ 9

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

искать глобальные объекты и исследовать их деструкторы.

Ответ 10

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

Это может быть де-распределение, но это просто дикая догадка. Что делают ваши деструкторы? Возможно, пейджинг как сумасшедший. Просто потому, что ваше приложение выделяет X объем памяти, это не значит, что он получит его. Скорее всего, это будет отключение виртуальной памяти. В зависимости от особенностей вашего приложения и ОС вы можете выполнять множество ошибок страницы.

В таких случаях это может помочь запустить iostat и vmstat на фоне, чтобы узнать, что происходит. Если вы видите много ввода-вывода, что верным признаком является ошибка страницы. Операции ввода-вывода всегда будут дороже, чем операторы памяти.

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

Запустите vmstat и iostat, как только вы получите сообщение о завершении, и посмотрите на любые указания на бананы ввода-вывода.

Ответ 11

Объекты в памяти организованы в кучу. Они не удаляются сразу, они удаляются один за другим, а стоимость удаления объекта - O (log n). Освобождение их отнимает loooong.

Тогда ответ будет да, это займет так много времени.

Ответ 12

Вы можете избежать вызова free на объект с помощью вызова деструктора my_object->~my_class() вместо delete my_object. Вы можете избежать free для всех объектов класса путем переопределения и обнуления operator delete( void * ) {} внутри класса. Производные классы с виртуальными деструкторами наследуют это delete, иначе вы можете скопировать-вставить (или, может быть, using base::operator delete;).

Это намного проще, чем вызов exit. Просто убедитесь, что вам не нужна эта память!

Ответ 13

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

Как вы узнали, виновна ли карта?

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

Если выход вашего процесса идет быстрее, тогда у вас есть ваш преступник.

Почему это так sloooooow?

Теперь, просто прочитав сообщение, для вашей неупорядоченной карты, я вижу потенциальные распределения для:

  • выделенные строки
  • элементы списка (каждая из которых является строкой + другие вещи)
  • неупорядоченные элементы карты + массив ведра

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

Другие причины?

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

Теперь деструкторы могут потребовать времени по другой причине.

Например, в Visual С++ 2008 в режиме отладки, например, при уничтожении итераторов STL, деструктор проверяет, что итераторы по-прежнему верны. Это привело к значительному замедлению разрушения моего объекта (который был в основном деревом узлов, каждый node имеющий список дочерних узлов с итераторами всюду).

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

Ответ 14

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

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

Ответ 15

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

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

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

У меня на самом деле была проблема, когда выделение памяти было быстрее, чем выделение ее, а после выделения памяти и последующего выделения ее у меня была утечка памяти. В конце концов, я решил, что именно поэтому.