class ObjectGraphWalker {

    long walk(int maxDepth, boolean abortWhenMaxDepthExceeded, Object... root) {
        StringBuilder traversalDebugMessage = null;
        long result = 0;
        boolean warned = false;
        try {
            Stack<Object> toVisit = new Stack<Object>();
            IdentityHashMap<Object, Object> visited = new IdentityHashMap<Object, Object>();

            if (root != null) {
                if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled()) {
                    traversalDebugMessage = new StringBuilder();
                    traversalDebugMessage.append("visiting ");
                }
                for (Object object : root) {
                    nullSafeAdd(toVisit, object);
                    if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled() && object != null) {
                        traversalDebugMessage.append(object.getClass().getName())
                            .append("@").append(System.identityHashCode(object)).append(", ");
                    }
                }
                if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled()) {
                    traversalDebugMessage.deleteCharAt(traversalDebugMessage.length() - 2).append("\n");
                }
            }

            while (!toVisit.isEmpty()) {
                warned = checkMaxDepth(maxDepth, abortWhenMaxDepthExceeded, warned, visited);

                Object ref = toVisit.pop();

                if (visited.containsKey(ref)) {
                    continue;
                }

                Class<?> refClass = ref.getClass();
                if (shouldWalkClass(refClass)) {
                    if (refClass.isArray() && !refClass.getComponentType().isPrimitive()) {
                        for (int i = 0; i < Array.getLength(ref); i++) {
                            nullSafeAdd(toVisit, Array.get(ref, i));
                        }
                    } else {
                        for (Field field : getFilteredFields(refClass)) {
                            try {
                                nullSafeAdd(toVisit, field.get(ref));
                            } catch (IllegalAccessException ex) {
                                throw new RuntimeException(ex);
                            }
                        }
                    }

                    long visitSize = calculateSize(ref);
                    if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled()) {
                        traversalDebugMessage.append("  ").append(visitSize).append("b\t\t")
                            .append(ref.getClass().getName()).append("@").append(System.identityHashCode(ref)).append("\n");
                    }
                    result += visitSize;
                } else if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled()) {
                    traversalDebugMessage.append("  ignored\t")
                        .append(ref.getClass().getName()).append("@").append(System.identityHashCode(ref)).append("\n");
                }
                visited.put(ref, null);
            }

            if (USE_VERBOSE_DEBUG_LOGGING && LOG.isDebugEnabled()) {
                traversalDebugMessage.append("Total size: ").append(result).append(" bytes\n");
                LOG.debug(traversalDebugMessage.toString());
            }
            return result;
        } catch (MaxDepthExceededException we) {
            we.addToMeasuredSize(result);
            throw we;
        }
    }

      ObjectGraphWalker(Visitor visitor, SizeOfFilter filter);

}