Если ГЦ добрался до А значит на него нет корневых ссылок
Этап выполнения финализаторов выполняется всегда раньше очистки аллоцированной памяти
То есть тут строгая детерминированность действий:
1. Нет корневых ссылок до конструкции А <-> Б
2. Сборщик мусора это видит и помечает А и Б для сборки
3. Сборщик мусора видит, что у Б есть финализатор - выполняет его. Б может писать в А
4. Сборщик мусора помечает память занятую А и Б как свободную
Про это у рихтера подробно есть
вопрос про "а если у А тоже есть финализатор, то что делать, это ж трекать придётся"