The message displays if the only reason the object is a GC root is that it's referenced by weak references, not if the only things that are referencing it are weak references. There would be no way to discover that an object has weak references pointing at
Weak references are confusingly named in a way: they aren't actually references at all. They work by creating a GC root for the object you specify. Unlike normal GC roots these don't force the object to stay in memory but just act as a placeholder. These aren't 'references' in the same way that assigning a variable creates a reference: it's two separate objects. One is usually (but not always!) a WeakReference object that knows about the GC root, and the other is the target object. 'Dereferencing' the weak reference involves looking up the object pointed to by the gc root.
Note that even with strong roots, there's no reason that an object which is a strong root can't have other references. If this were the case, you couldn't create a reference to an object stored in a static variable.
Also note that the WeakReference class isn't the only way to create a weak reference GC root: you can create them from unmanaged code, or using the GCHandle class.
The memory profiler won't show weak references in the object retention graph (as they aren't 'references' in the conventional sense), and won't count them in the distance to GC root column, so you can still use these to discover why the object is still in memory.
Red Gate Software Ltd.