No reason why the garbage collector wouldn't work on unsafe code. I'm assuming you're talking about pointers to pinned objects, like:
int[] arr = new int[100];
unsafe
{
fixed (int* p = arr)
{
// use p
}
}
At the end of the block p is unaccesible anymore, so it can be safely collected.
Now... this isn't always true. You might pass the pointer to other functions and then exit the block (the EnumWindows family of functions come to mind where you give them a pointer to a structure and then can be done with the function you're in, they handle the rest themselves).
The GC.KeepAlive
"function" (read hack) is used to handle this case by holding the variable in scope until you're really done with it -- that's right, it does nothing except trick the GC into thinking you're still using the reference.