1. Финализация вполне может быть отложена по усмотрению сборщика мусора
2. JIT вполне может так скомпилировать твой код, что вот этот = null будет опущен => при поиске ссылок, ссылка всё ещё будет на стеке.
Попробуй вынести в новый метод и посмотреть там.
Ну и такой код как у тебя в реальном мире лучше не использовать (это я про принудительный вызов гц, логи в финализаторе, и финализатор который не чистит неуправляемые ресурсы)
PS: читаем внимательно документацию и особенно слова "не гарантирует"
Используйте параметр , mode чтобы указать, должна ли сборка мусора происходить немедленно или только при оптимальном времени для освобождения объектов. Использование этого метода не гарантирует, что вся недоступная память в указанном поколении будет освобождена.
Чтобы настроить навязчивость сборки мусора в критические периоды в приложении, задайте LatencyMode свойство .
Сборщик мусора не собирает объекты с номером поколения, превышающим указанное параметром generation . Используйте свойство , MaxGeneration чтобы определить максимально допустимое generationзначение .
Чтобы сборщик мусора учитывал все объекты независимо от их создания, используйте версию этого метода, которая не принимает параметров.
Чтобы сборщик мусора отнимает объекты до указанного поколения объектов, используйте перегрузку GC.Collect(Int32) метода . При указании максимального поколения собираются все объекты.