Мастера DELPHI, Delphi programming community Рейтинг@Mail.ru Титульная страница Поиск, карта сайта Написать письмо 
| Новости |
Новости сайта
Поиск |
Поиск по лучшим сайтам о Delphi
FAQ |
Огромная база часто задаваемых вопросов и, конечно же, ответы к ним ;)
Статьи |
Подборка статей на самые разные темы. Все о DELPHI
Книги |
Новинки книжного рынка
Новости VCL
Обзор свежих компонент со всего мира, по-русски!
|
| Форумы
Здесь вы можете задать свой вопрос и наверняка получите ответ
| ЧАТ |
Место для общения :)
Орешник |
Коллекция курьезных вопросов из форумов
KOL и MCK |
KOL и MCK - Компактные программы на Delphi
Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / rsdn.org

 
Чтобы не потерять эту дискуссию, сделайте закладку « предыдущая ветвь | форум | следующая ветвь »

Поиск утечки памяти и TXMLDocument


Eraser ©   (21.02.17 04:32

Привет, товарищи.
Который день пытаюсь побороть/найти утечку памяти, связанную с использованием TXMLDocument.

Вот тестовый код:
class procedure TTestXmlThread.ProcessTest;
const
 MY_XML_ENCODING = 'UTF-8';
var
 XMLDoc: IXMLDocument;
 RootNode: IXMLNode;
 XmlRootName: string;
 Version: Integer;
 msData: TMemoryStream;
begin
 XmlRootName := 'test_root_node' + Random(MaxInt).ToString;
 Version := Random(MaxInt);

 msData := TMemoryStream.Create;
 XMLDoc := TXMLDocument.Create(nil);
 try
   //(XMLDoc as TXMLDocument).DOMVendor := DOMVendors.Find('Omni XML');

   // Инициализируем XML-документ.
   XMLDoc.LoadFromXML('<?xml version="1.0"?><' +
     XmlRootName + ' version="' +
     Version.ToString +'"></' +
     XmlRootName + '>');

   XMLDoc.Encoding := MY_XML_ENCODING;

   // Формируем XML.
   RootNode := XMLDoc.DocumentElement;

   //SerializeToNode(RootNode);

   msData.Size := 0;
   XMLDoc.SaveToStream(msData);

   OutputDebugString(PChar(XmlRootName));
 finally
   XMLDoc := nil;
   FreeAndNil(msData);
 end;
end;


Вот ссылка на тестовый пример https://dl.dropboxusercontent.com/u/26403307/XMLMemLeak.zip (в архиве исходник проекта с примером и подключенный к нему свежий FastMM). Важно! В настройках FastMM включена опция {$define AlwaysClearFreedMemory}, это гарантирует принудительное затирание нулями всех освобождаемых блоков памяти.

Суть проблемы вот в чем - если выполнить данный код, то в выделенной памяти приложения остаются фрагменты, выглядящие примерно вот так https://dl.dropboxusercontent.com/u/26403307/xmlmemleak.png

(Отдельной строкой безмерная благодарность Rouse_ за удобную утилиту processmm)

Стройки в памяти связанны именно с TXMLDocument. При этом, объект TXMLDocument, который реализует интерфейс IXMLDocument, конечно же, освобождается, его деструктор и деструкторы вложенных нодов срабатывают.

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

Еще одно замечание, проблема воспроизводится не только на движке MSXML, но и на других тоже (например, Omni XML). Т.е., наверное, дело в абстракции TXMLDocument, только вот не ясно где именно.

Честно гуглил - в таком криминале TXMLDocument не был замечен.


rrrrr ©   (21.02.17 08:59[1]

а если TXMLDocument.Create(something_not_nil) + Free ?


DayGaykin ©   (21.02.17 13:59[2]

А reportmemoryleaksonshutdown :=  true что говорит?

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


Eraser ©   (21.02.17 16:03[3]


> rrrrr ©   (21.02.17 08:59) [1]

это может привести и приведет к AV в высоконагруженном многопоточном приложении.

> something_not_nil

destructor TXMLDocument.Destroy;
begin
 Destroying;
 if FOwnerIsComponent and Active and Assigned(FDocumentNode) and (FRefCount > 1) then
   (FDocumentNode as IXMLNodeAccess).ClearDocumentRef;
 SetActive(False);
 FreeAndNil(FXMLStrings);
 inherited;
end;

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


> DayGaykin ©   (21.02.17 13:59) [2]
> А reportmemoryleaksonshutdown :=  true что говорит?

молчание.

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


rrrrr ©   (21.02.17 16:33[4]

это может привести и приведет к AV в высоконагруженном многопоточном приложении.


это не может ни к чему привести.

txmldocument хитрый класс.

Create(nil) = ведет себя как интерфейсная ссылка, free делать нельзя.
Create(owner) = ведет себя как чистый потомок TObject, деструктор вызываешь сам.


rrrrr ©   (21.02.17 16:37[5]

а вообще txmldocument - для школьников.

uses msxml2_tlb
CoDomDocument.create


Eraser ©   (21.02.17 16:51[6]


> rrrrr ©   (21.02.17 16:33) [4]


> uses msxml2_tlb

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


> Create(owner) = ведет себя как чистый потомок TObject, деструктор
> вызываешь сам.

не TObject, а TComponent, там есть разница, нужна защита создания тогда, буду пробовать.


Eraser ©   (22.02.17 13:45[7]

Прикрутил самописный менеджер памяти в виде прямой обертки над heap. Рост потребления заметно уменьшился. Скорее всего, все таки, проблема не в утечке памяти, а в жесткой фрагментации. Надо будет еще попробовать msvcrt.


Rouse_ ©   (22.02.17 17:17[8]

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


Eraser ©   (22.02.17 23:26[9]


> Rouse_ ©   (22.02.17 17:17) [8]

задумываюсь над тем, чтобы парсинг/сериализацию XML (а память "утекает", по всей видимости, через строки, в основном, ну и через instance'ы сопричастных объектов) разместить в отдельном утилитарном процессе (который можно смело рестартовать или запускать несколько копий), и скармливать этому процессу через IPC данные для парсинга, но там есть много НО.

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

на чистой heap мне пока нравится как работает, но нужно время на тесты.

PS
вот после 12 часов работы натравил ProcessMM на свой процесс с менеджером памяти на куче, который в занимает жалких 1,5 Гб на данный момент. processmm64.exe отожрал 10 Гб ОЗУ и, видимо, подвис. Подождал его минуты 2. обычно, процесс с таким объемом памяти ProcessMM открывает за пару тройку секунд. это говорит о том, что эти 1,5 Гб - мельчайшие фрагменты в миллионах нодов кучи.

PPS
все таки надо будет протестировать майкросовтовский msvcrt.


Eraser ©   (22.02.17 23:29[10]


> Rouse_ ©   (22.02.17 17:17) [8]

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


Rouse_ ©   (23.02.17 00:01[11]

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


Rouse_ ©   (23.02.17 12:59[12]

ЗЫ: вот обрати внимание, я там специально их группами выделял: https://dl.dropboxusercontent.com/u/70911765/heap.png

вот а еще, зайди в настройки и сними галку "Show detailed heap" - это достаточно тяжелая операция, вероятно на ней и подвис


Eraser ©   (23.02.17 14:32[13]


> Rouse_ ©   (23.02.17 12:59) [12]


> сними галку "Show detailed heap"

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

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


Rouse_ ©   (23.02.17 14:38[14]

Так у тебя еще и сервер? Конечно пересматривать нужно, у меня сервера годами работают, не шучу


Eraser ©   (23.02.17 15:55[15]


> Rouse_ ©   (23.02.17 14:38) [14]

да, там около 100000 tcp соединений, периодически с некоторыми идет обмен сообщениями на базе xml. но сам транспорт хорошо закэширован, а вот с XML и с тем, что связано со строками беда.
до поры до времени все тоже работало, если не годами, то месяцами, остановки были только по причине апдейта системы или самого ПО. однако с ростом нагрузки появилась обозначенная проблема.


Rouse_ ©   (23.02.17 16:29[16]

слушай, а свяжись ка ты с женькой jack128, в понедельник могу состыковать. Он у нас с этой проблемой маялся в свое время и в итоге накидал какой-то простенький класс (банально с MSDN скомуниздил) там быстрая загрузка XML вроде бы даже без построения DOM.


Rouse_ ©   (23.02.17 16:34[17]

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


Eraser ©   (24.02.17 03:05[18]


> Rouse_ ©   (23.02.17 16:29) [16]

благодарю за наводку, уточню.

а есть какая-нибудь проверенная информация по поводу Segment Heap?

https://www.blackhat.com/docs/us-16/materials/us-16-Yason-Windows-10-Segment-Heap-Internals-wp.pdf


Rouse_ ©   (07.03.17 15:38[19]

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

ЗЫ: кстати обнови утилитку, я там добавил кучу удобностей (мне как раз по работе для разбора качества работы ВМ потребовалось), дизасм стал выглядеть гораздо удобней и проще для анализа (он теперь отладочные МАР кушает в легкую + реконнект к процессу сделал ну и работа с GUI гораздо ускорилась для поиска проблемы):
https://github.com/AlexanderBagel/ProcessMemoryMap/raw/master/img/10.png
https://github.com/AlexanderBagel/ProcessMemoryMap/raw/master/img/11.png
Релиз тут: http://rouse.drkb.ru/winapi.php#pmm2


Eraser ©   (10.03.17 11:30[20]


> Rouse_ ©   (07.03.17 15:38) [19]


> Только сейчас обратил внимание что ты спрашивал, а зачем
> тебе это надо?

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


версия для печати

Написать ответ

Ваше имя (регистрация  E-mail 







Разрешается использование тегов форматирования текста:
<b>жирный</b> <i>наклонный</i> <u>подчеркнутый</u>,
а для выделения текста программ, используйте <code> ... </code>
и не забывайте закрывать теги! </b></i></u></code> :)


Наверх

  Рейтинг@Mail.ru     Титульная страница Поиск, карта сайта Написать письмо