TGViewer
.NET epeshk blog .NET epeshk blog @epeshkblog · 2.67K subscribers
Post #155 4.25K
GC Hole: чем ref отличается от указателя

GC Hole — баг, ситуация, когда в программе образуется ссылка на объект, уже собранный или перемещённый сборщиком мусора. Часто встречается в коде, который решили "микрооптимизировать".

Я никогда не встречал подобного бага вне кода, найденного в интернетах. Предположу, что его допускают в первую очередь программисты, переходящие на C# с языков с ручным управлением памятью — там аналогичная проблема называется use-after-free. По сути, GC Hole — частный случай use-after-free, только освобождение памяти выполняется не вручную, а сборщиком мусора.

Причина бага — попытка работать со ссылками на managed объекты как с адресами в памяти (числами).
Пример: long address = (long)Unsafe.AsPointer(ref value);

Пока на объект ссылаются обычными ссылками, или через `ref` — GC отслеживает эти ссылки и не будет собирать объект как мусор. При дефрагментации (compaction) кучи GC переместит объекты на новые места в памяти и обновит отслеживаемые им ссылки.

void*, int*, IntPtr, long и подобные типы — просто числа, GC про них ничего не знает. В итоге, если между получением адреса и его использованием случится GC — программа может обратиться уже по некорректному адресу и прочитать/перезаписать данные из случайного места. Потенциально, GC Hole — уязвимость, security bug.

Проблема может долго оставаться незамеченной, например, просто везёт и GC не успевает сработать, или ссылка ведёт в Large Object Heap, который не дефрагментируется в нормальном режиме.

Как избежать GC Hole:
- не пытаться превращать managed ссылки в адреса-числа

- не делать оптимизаций, которые дают выигрыш по производительности в пределах погрешности: обычно нет смысла использовать указатели, чтобы убрать проверки на выход за границы массива и выиграть 0.01%

- если очень надо, например для интеропа, использовать пиннинг (запрет GC перемещать объект при дефрагментации), с помощью fixed или GCHandle.Alloc(obj, GCHandleType.Pinned). Долгоживущие массивы можно аллоцировать в Pinned Object Heap

- осторожно работать с ref. ref может указывать на элемент массива или поле объекта

- использовать методы, работающие с ref, которые позволяют обойтись без перехода к адресам-числам:
- Unsafe.IsAddressLessThan(ref a, ref b) вместо Unsafe.AsPointer(ref a) < Unsafe.AsPointer(ref b)
- Unsafe.Add/Subtract, Unsafe.ByteOffset и подобные вместо арифметики указателей.

@epeshkblog | Поддержать канал
Telegram .NET epeshk blog Канал с заметками о C# и .NET Поддержать канал: https://t.me/blog_donate/2 Обратная связь: https://forms.gle/3uRz7FmzUA26Kw4y5
  • 👍 63
  • 🤩 3
  • 🔥 1
More from @epeshkblog
  1. Sep 23, 2026На какие доклады я пойду на DotNext 2026. Обзор программы В этом году конференция почти на…
  2. Sep 22, 2026Приходите на доклад https://dotnext.ru/talks/20010542-modularity-without-microservices-in-…
  3. Sep 22, 2026В этом году не выступаю, надо отдохнуть, и переосмыслить разработку после всеобщего успеха…
  4. Sep 22, 2026#видеозаписи До нового DotNext осталось совсем немного, а по дороге на конференцию — можно…
  5. Sep 9, 2026.NET и Владимир: отличный повод совместить митап и выходные в городе с историей Офлайн-мер…
  6. Aug 3, 2026LLM оказались упорнее людей 2 недели назад LLM впервые справилась с математической задачей…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →