It just needs to be accessible from a root (eg. a global variable or a data structure that has a longer lifespan than the object it references). Most programs use global variables or classes that contain pointers to major data structures so that they are easily accessible. Another common thing is to build auxiliary data structures to augment an existing data structure. This often involves pointers going both ways, so they become intertwined. At some point you've done the work and want to delete the auxiliary data structure. Now how do you do this in a GC world?
The key problem here is that a data structure may be referred to from many places, so if you want to remove it you really have to remove all references to it.
There is a difference. In the malloc/free case you only need to call free once on an object and it will be reused, even if some other data structures still point to it (if they actually access it, it is a bug of course). In the GC case you either have to clean up all dangling references, or hope all they will go out of scope soon.
You're right that with correct design and correct variable scoping (especially of the roots that point to big data structures) and avoiding copying pointers all over the place (or intertwining data structures as described above), you can get GC to work perfectly. But all this requires extra programming effort.
You can do the same with explicit memory management. I often use what you could describe as explicit garbage collection, which is allocate all data for a datastructure in a single memory block, and release it in one go when done with it, thus avoiding a complex traversal.
Wilco