collectFieldLoaders
Every loader one field deep from o: per field, the loader that defined its value — or the value itself, when it already is a loader.
One field deep is the whole search, not a depth limit on a graph walk. Both shapes put their loader in a field of the object that exists to hold it, and going deeper needs cycle detection and array/collection traversal for the same answer: measured live, depth 1 reaches 151 objects and depth 10 reaches 134k, both finding exactly the same loaders.
Fields, not getters — a field read has no side effects, and this walks another plugin's internals. stopAt keeps it inside classes that can be opened: ClassLoader.class for a loader, since setAccessible on java.lang.ClassLoader's own fields throws.