public static <V> boolean walkObjects(int maxDepth,
                                        @NotNull Map<Object, String> startRoots,
                                        @NotNull Class<V> lookFor,
                                        @NotNull Predicate<Object> shouldExamineValue,
                                        @NotNull PairProcessor<? super V, ? super BackLink<?>> leakProcessor) {
    IntSet visited = new IntOpenHashSet(100);
    Deque<BackLink<?>> toVisit = new ArrayDeque<>(100);

    for (Map.Entry<Object, String> entry : startRoots.entrySet()) {
      Object startRoot = entry.getKey();
      String description = entry.getValue();
      toVisit.addLast(new BackLink<Object>(startRoot, null, "(root)", null) {
        @Override
        void print(@NotNull StringBuilder result) {
          super.print(result);
          result.append(" (from ").append(description).append(")");
        }
      });
    }

    while (true) {
      BackLink<?> backLink = toVisit.pollFirst();
      if (backLink == null) {
        return true;
      }

      if (backLink.depth > maxDepth) {
        continue;
      }
      Object value = backLink.value;
      if (lookFor.isAssignableFrom(value.getClass()) && markLeaked(value) && !leakProcessor.process((V)value, backLink)) {
        return false;
      }

      if (visited.add(System.identityHashCode(value))) {
        queueStronglyReferencedValues(toVisit, value, backLink, shouldExamineValue);
      }
    }
  }
public static <V> boolean walkObjects(int maxDepth,
                                        @NotNull Map<Object, String> startRoots,
                                        @NotNull Class<V> lookFor,
                                        @NotNull Predicate<Object> shouldExamineValue,
                                        @NotNull PairProcessor<? super V, ? super BackLink<?>> leakProcessor)