You are right in some cases. The GC looks through the heap pessimistically - i.e. it sets off assuming everything (in Generation 0) will be GCed.
It literally goes through everything on the heap through a first sweep called "marking", in which is checks if anything is referencing it. Since they are all reference types and some reference others, it will recursively navigate the references. Don't worry - there is logic to not get into an infinite loop!
If it finds an object is not referenced, it will firstly mark it, by setting a flag within the object called the sync block index.
After going through every object on the heap, it will then begin a process called "compacting" which is when it shifts all of the remaining objects into the same area of memory, leaving the memory above clear. It will keep the objects of the same generation together as they are statistically more likely to be de-referenced at the same time.
This therefore will reduce the memory needed.
Garbage Collection doesn't necessarily speed up your program, but does allow it to re-use the space occupied by unused objects.
There are many many articles on the subject. I personally like "CLR via C#" by Jeffrey Richter who gives an excellent chapter on how it works.