Нужна помощь в интерпретации итоговой кучи WinDbg для устранения утечки памяти - PullRequest
0 голосов
/ 21 апреля 2019

Этот вопрос очень похож на: расследование утечки памяти windbg - отсутствует куча памяти

За исключением того, что в моем случае все x86, тогда как ответ на этот пост говорит, что Windbg x64 не работает.

В моем случае, когда я делаю "! Heap -s", я получаю:

************************************************************************************************************************
                                              NT HEAP STATS BELOW
************************************************************************************************************************
LFH Key                   : 0x653c3365
Termination on corruption : DISABLED
  Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                    (k)     (k)    (k)     (k) length      blocks cont. heap 
-----------------------------------------------------------------------------
00e70000 00000002  761224 757296 761012   6306  1149    51    0  572b1   LFH
00d60000 00001002    1292    128   1080     26     7     2    0      0   LFH
01050000 00001002    1292   1048   1080    271    18     2    0     31   LFH
..snip..

Где меня интересует куча на 00e70000.

Далее при выполнении команды:! Heap -stat -h 00e70000 -grp s 0n999

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

0:000> !heap -stat -h 00e70000 -grp s 0n999
 heap @ 00e70000
group-by: TOTSIZE max-display: 999
    size     #blocks     total     ( %) (percent of total busy bytes)
    1000 14a - 14a000  (20.24)
    600c 16 - 84108  (8.10)
    168 408 - 5ab40  (5.56)
    154 404 - 55550  (5.23)
    10d8 2a - 2c370  (2.71)
    24 113f - 26cdc  (2.38)
    22750 1 - 22750  (2.11)

Затем я вставляю все это в excel, преобразую 3-й столбец в десятичную и суммирую, получая всего 6,5 мегабайт или около того.

Оба! Address -summary, а также! Heap -s указывают на то, что я должен получить сумму чего-то около 808 мегабайт. Это наводит меня на мысль, что либо я не понимаю единицы команды -stat, либо, возможно, оба x64 и x86 (весь Windbg) сломаны, либо у меня есть более фундаментальное недоразумение.

Может ли кто-нибудь помочь мне понять, в чем дело?

Спасибо!

Редактировать: Дополнительная информация Используя DebugDiag, я вижу, что основная (по умолчанию) куча имеет 46/54 сегментов, которые имеют общую функцию, все они имеют размер 15,81 мегапикселя, и все они почти полностью выделены. Это общая разница, по которой я скучаю.

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

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

Второе редактирование, Дополнительная информация: После удаления FASTMM из нашей кодовой базы и повторного запуска тестов я вижу, что сегменты объемом 15,81 МБ все еще существуют и все еще протекают. Их можно увидеть в анализе DebugDiag как:

Segment Information
Base Address    Reserved Size   Committed Size  Uncommitted Size    Number of uncommitted ranges    Largest uncommitted block   Calculated heap fragmentation
0x00e70000  1020 KBytes 1020 KBytes 0 Bytes 1   0 Bytes 0%  0
0x03be0000  1020 KBytes 1020 KBytes 0 Bytes 1   0 Bytes 0%  0
0x04a20000  2 MBytes    2 MBytes    0 Bytes 1   0 Bytes 0%  0
0x051e0000  4 MBytes    4 MBytes    0 Bytes 1   0 Bytes 0%  0
0x0c4b0000  8 MBytes    8 MBytes    0 Bytes 1   0 Bytes 0%  0
0x19dc0000  15.81 MBytes    15.78 MBytes    28 KBytes   1   28 KBytes   -11928.57%  Unavailable
0x1c3b0000  15.81 MBytes    15.81 MBytes    0 Bytes 1   0 Bytes 0%  0
0x2c900000  15.81 MBytes    15.81 MBytes    0 Bytes 1   0 Bytes 0%  0
..snip..

, где новые разделы, показанные внизу, отмеченные как 15,81 МБ, расширяются для дополнительных 46 новых сегментов и представляют 727,26 МБ утечки памяти в неуправляемых кучах.

Поиск по значению 15,81 МБ приводит меня к нескольким различным ссылкам, касающимся среды выполнения Microsoft VC:

https://social.msdn.microsoft.com/Forums/vstudio/en-US/e7534d01-57ed-455c-bc0d-edb1b87d0f52/microsoft-vc-runtime-heap-fragmentation?forum=vclanguage https://docs.microsoft.com/en-us/visualstudio/debugger/crt-debug-heap-details?view=vs-2019 Debugdiag показывает «кучу времени выполнения Microsoft VC», используя более 1 ГБ

Используя Windbg, я могу отобразить информацию о распределении относительно распределений, которая выглядит следующим образом:

    61f130c8: 08008 . 10008 [101] - busy (10000) Internal 
    61f230d0: 10008 . 10008 [101] - busy (10000) Internal 
    61f330d8: 10008 . 10008 [101] - busy (10000) Internal 
    61f430e0: 10008 . 08008 [101] - busy (8000) Internal 
    61f4b0e8: 08008 . 10008 [101] - busy (10000) Internal 
    61f5b0f0: 10008 . 10008 [101] - busy (10000) Internal

Однако, поскольку они помечены как «Внутренние», они не участвуют в «трассировке стека» (опция -ust gflags) для определения фактического кода, который был выполнен для их распределения.

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

1 Ответ

1 голос
/ 28 апреля 2019

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

В оригинальном посте я показал раздел анализа DebugDiag, который отображал постоянно растущее число 15,81сегменты в куче процесса по умолчанию.Я пришел к выводу, что это просто способ Windows, позволяющий кучи (в системе, возможно, из множества кучи) расти, не делая никаких предположений о том, какой куче нужно будет расти в ситуации большой нагрузки.Кажется, они создаются с сегментом 1 МБ, затем с другим, затем с сегментом 2 МБ, затем с 4 МБ, 8 МБ и 16 МБ.После этого они только увеличиваются на 16 МБ (т. Е. 15,81) по мере необходимости.

Когда утечка в "родной куче" происходит, сегменты добавляются снова и снова, как это.

Прежде чем вопросы здесь даже начались,мы провели анализ управляемой памяти, используя файлы дампа, сделанные в различные моменты расширенного 92-часового нагрузочного теста.Мы использовали как Visual Studio, так и SOS-команды Windbg, и не обнаружили «управляемого» роста.Единственная проблема заключалась в том, что неуправляемый код протекал, как показано в исходном сообщении.

В тот момент мы использовали «Gflags + ust» в процессе, чтобы получить обратные трассировки стека.Это дало нам полностью достоверную информацию, но недостаточно данных.Он показал большое количество пропущенных блоков и утверждал, что они были выделены SecureString.ctor.Не видя в управляемой куче ни одной SecureStrings (живой или мертвой), мы решили не обращать внимания на то, что она говорила нам в то время.

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

Как только мы обнаружили утечку API, мы изменили серверпо существу, «отсеивать» большие дорожки кода, который он выполнял, и повторять анализ отладки, пока наш «потрошенный код» больше не обнаружил утечку.

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

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

Это передавалось методу для его декодирования, который содержал примерно такой код:

//Convert to IntPtr using marshal
IntPtr tmp = Marshal.SecureStringToBSTR(SecureString_Param1);
//convert to string using marshal
string plain = Marshal.PtrToStringAuto(tmp);
//Return the now plain string
return plain;

Утечка памяти была неуправляемым BSTR, который фактически был изначально выделен SecureString.ctor.Этот код был проверен в отдельном тестовом приложении для проверки.

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

...