Why would the actual freeing of memory need to synchronous with the running of the destructors?
It doesn't, but with the proposed gc the pike runtime wouldn't have any earlier chance to discover the garbage data.
Ideally I'd expect the destructors to be called just like in C++: whenever the variables go out of scope.
The problem is more complicated in Pike since everything is allocated on the heap. In C and C++ you can allocate a struct on the stack, and so it'll be naturally freed when the stack is popped (with the risk of loose pointers instead). There's no way to do that in Pike - the mutex lock is allocated on the heap and only a reference to it is on the stack.
This can be solved at the compiler level, without changes in Pike applications. It also then supports the level of least surprise, which is always a good thing.
In very trivial cases the compiler can determine that the object doesn't get refs from elsewhere, e.g:
void foo() { Foo x = Foo(); bar (y); }
However, if you do just
void foo() { Foo x = Foo(); bar (x); }
or
void foo() { Foo x = Foo(); x->beep(); }
then the compiler can no longer know if x has grown more references during the bar or beep calls. For that to work, the compiler would have to analyze what every function does with this_object() and all its arguments. It's probably solvable, but it would require a fair bit of work in the compiler, and every C level function would have to declare whether or not it adds external refs to the current storage and every argument.