Причины сбоя при сборке мусора - PullRequest
6 голосов
/ 28 сентября 2011

В течение некоторого времени я боролся со сбоем в приложении C #, которое также использует значительную долю модулей C ++ / CLI, которые в основном являются оболочками вокруг собственных библиотек для доступа к драйверам устройств. Авария не всегда легко воспроизводима, но мне удалось собрать полдюжины аварийных дампов, которые показывают, что программа всегда аварийно завершает работу с нарушением доступа во время сборки мусора. Это родной стек вызовов и журнал последних событий:

0:000> k
ChildEBP RetAddr  
0012d754 79f95a8f mscorwks!WKS::gc_heap::find_first_object+0x62
0012d7dc 79f933bb mscorwks!WKS::gc_heap::mark_through_cards_for_segments+0x493
0012d814 79f92cbf mscorwks!WKS::gc_heap::mark_phase+0xc3
0012d838 79f93245 mscorwks!WKS::gc_heap::gc1+0x62
0012d84c 79f92f5a mscorwks!WKS::gc_heap::garbage_collect+0x253
0012d878 79f94e26 mscorwks!WKS::GCHeap::GarbageCollectGeneration+0x1a9
0012d904 79f926ce mscorwks!WKS::gc_heap::try_allocate_more_space+0x15b
0012d918 79f92769 mscorwks!WKS::gc_heap::allocate_more_space+0x11
0012d938 79e73291 mscorwks!WKS::GCHeap::Alloc+0x3b

0:000> .lastevent
Last event: 7e8.88: Access violation - code c0000005 (first/second chance not available)
  debugger time: Mon Sep 26 11:34:53.646 2011 (UTC + 2:00)

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

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

Я уже пытался комментировать весь «опасный» код C ++ / CLI (особенно части, которые используют закрепленные маркеры), но это не помогло. Пытаясь найти шаблон в памяти, который был перезаписан, я посмотрел на разобранный код в момент сбоя:

0:000> u .-a .+a
mscorwks!WKS::gc_heap::find_first_object+0x54:
79f935b9 89450c          mov     dword ptr [ebp+0Ch],eax
79f935bc 8bd0            mov     edx,eax
79f935be 8b02            mov     eax,dword ptr [edx]
79f935c0 83e0fe          and     eax,0FFFFFFFEh
79f935c3 f70000000080    test    dword ptr [eax],80000000h      <<<<CRASH
79f935c9 0f84b1000000    je      mscorwks!WKS::gc_heap::find_first_object+0x73

0:000> r
eax=00000000 ebx=01c81000 ecx=01c80454 edx=01c82fe0 esi=012f0000 edi=000027e1
eip=79f935c3 esp=0012d738 ebp=0012d754 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mscorwks!WKS::gc_heap::find_first_object+0x62:
79f935c3 f70000000080    test    dword ptr [eax],80000000h ds:0023:00000000=????????

Сбой происходит при попытке разыменования регистра EAX, который является нулевым. Теперь из того, что я вижу, EAX был загружен из содержимого, на которое указывает регистр EDX, поэтому я посмотрел на адрес, сохраненный там:

0:000> dd @edx-10
01c82fd0  06542778 00000000 00000000 01c82494
01c82fe0  00000000 00000000 00000000 00000000
01c82ff0  01b641d0 00000000 00000000 01c82380

РЕДАКТИРОВАТЬ: Теперь я вижу, что мой анализ был неправильным, не хватает понимания режимов адресации x86.

Итак, я вижу, что начиная с адреса 01c82fed (значение, хранящееся в EDX) следующие 16 байтов равны нулю. Но, глядя на другой похожий аварийный дамп, я вижу следующее:

0:000> dd @edx-10
018defd4  00000000 00000000 00000000 00000000
018defe4  00000000 00000000 018b468c 01742354
018deff4  00e0907f 00000000 00000000 00000000

Итак, здесь 16 байтов перед адресом, указанным EDX, и следующие 8 оттуда равны нулю. И то же самое происходит с другими имеющимися у меня дампами, я не вижу здесь паттерна, то есть не похоже, что какой-то фрагмент кода просто перезаписывает эту область памяти.

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

(могут ли заколотые ручки вызвать проблему? У нас их довольно много, и я думаю, что забавно то, что я всегда вижу 137 - не больше и не меньше - закрепленных ручек с! Gchandles в точке крушение, это странное совпадение для меня ..).

EDIT : забыл упомянуть, что мы используем версию 3.5 .Net Framework. Я вижу сообщения о подобных сбоях в .Net 4, когда фоновый GC активен (где-то есть упоминание, что это ошибка в .Net), но я не думаю, что это уместно здесь, так как AFAIK нет фонового GC в .Net 3.5.

Ответы [ 3 ]

2 голосов
/ 05 октября 2011

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

2 голосов
/ 28 сентября 2011

Не уверен, поможет ли это, но обычно не используют деструкторы и не позволяют GC обрабатывать неуправляемую память.Вместо этого используйте шаблон Dispose и вместо этого переместите весь код деструктора в финализаторы:

ref class MyClass
{
  UnsafeObject data;
  MyClass()
  {
    data = CreateUnsafeDataObject();
  }
  !MyClass()  // IDisposable.Dispose()
  {
    DeleteUnsafeDataObject(data);
  }
  ~MyClass()  // Destructor
  {

  }
}

Это позволит реализовать шаблон IDisposable для объекта.Позвоните в Dispose, чтобы очистить неуправляемые данные, и в худшем случае у вас будет больше шансов выяснить, что именно происходит.

0 голосов
/ 28 сентября 2011

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

...