I'm wondering why weak references are not built-in types which are treated similar to standard object references by the garbage collection. In C#, you must either use the WeakReference class (which has a significant performance and memory impact as it is created on the managed heap itself and uses a finalizer for cleanup) or manually allocate a GCHandle (which later has to be freed manually again).
In principle, I would implement the GC for weak references in the following way:
To identify the live objects, the GC has to walk the object tree starting from all roots. If it finds a reference to an object, the object is marked as reachable. In contrast, if it finds a weak reference to an object, this would be ignored in the first step.
In the second step, the GC looks where the unreachable memory blocks are located and compacts the managed heap (or at least parts of it). When it does this, it has to walk the object tree again and update all references (normal and weak) to objects which have been moved. Additionally, if it finds a weak reference to an object not marked as reachable, it has to be set to null (or some other well defined invalid memory location) to mark it as invalid.