медленное высвобождение памяти (пересчитанная структура) — является ли мой обходной путь хорошим способом?

#delphi #delphi-2007 #refcounting

#delphi #delphi-2007 #пересчет

Вопрос:

в моей программе я могу загрузить каталог: iCatalog

Каталог здесь содержит много пересчитанных структур (Icollections IItems, IElements, iRules и т. Д.)

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

мой вопрос :

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

Я еще не тестировал его, но я намерен создать новый поток с :

 ErazerThread.OldCatalog := Catalog; // old catalog refcount jumps to 2
Catalog := LoadNewCatalog(...);     // old catalog refcount =1
ErazerThread.Execute;               //just set OldCatalog to nil.
  

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

Безопасно ли это (и хорошая практика)?
Есть ли у вас примеры существующего кода, который уже выполняет выпуск с помощью аналогичного метода?

Комментарии:

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

2. Вы использовали профилировщик и выяснили, ПОЧЕМУ бесплатный код занимает так много времени? Одни только операции с кучей, вероятно, НЕ вызывают вашей проблемы, если вы не освобождаете 1,8 ГБ памяти, по 100 байт за раз.

3. @WarrenP Используя профилировщик выборки и завершая выпуск с помощью OutputDebugString (‘SAMPLING ON’); (и ВЫКЛЮЧЕН), я обнаружил, что 95,05% времени происходит в ntdll.dll … это не имеет для меня никакого полезного значения .. (тогда может ли анализ оставшихся 4,54% в моем исполняемом файле бытьинтересно для оптимизации?)

4. Уоррен П.: разве управление COM-памятью не медленнее, чем родной? Речь идет о распределении интерфейсов. Не знаю, насколько медленнее.

5. @DamienD: я удивлен, что выделение памяти занимает так много времени внутри ntdll.dll . Он должен сильно меняться местами? Сколько у вас физической оперативной памяти?

Ответ №1:

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

Однако обратите внимание, что если освобождение затрагивает блокировку, которую использует ваш диспетчер памяти (например, глобальную блокировку heapmanager), то это бесполезно, поскольку ваш mainthread заблокируется при первом доступе к heapmanager.

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

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

(*) Самый простой способ — добавить его в tthreadlist и использовать tevent для сигнализации о том, что элемент был добавлен.

Комментарии:

1. Я понимаю вашу точку зрения, но что касается потоков и низкоуровневого управления памятью, я, хм, новичок … поправьте меня, если я ошибаюсь: глобальная блокировка heapmanager может быть вызвана потокобезопасным объектом? (я имею в виду использование критических разделов?)

2. Потокобезопасный объект передается в memorymanager при освобождении. MemoryManager может вызываться из нескольких потоков, поэтому имеет блокировки. Может быть, мне следует обобщить ответ следующим образом: «Если перемещение по дереву, подсчет ссылок и деструкторы оказываются основным фактором замедления, может помочь переход к потоку. Если это работа по освобождению в heapmanager (или вызовы COM, если они являются COM-объектами), это может быть не так »

3. попробовал. переход к потоку в моем случае не помогает (см. Мой комментарий под моим вопросом). спасибо, что объяснили, почему.

Ответ №2:

Это выглядит нормально, но не вызывайте метод потока Execute напрямую; это запустит код объекта потока в текущем потоке вместо того, который создает объект потока. Start Resume Вместо этого вызовите or .