code: package objectexplorer;
  
  import com.google.common.base.Preconditions;
  import com.google.common.base.Predicate;
  import com.google.common.base.Predicates;
  import com.google.common.collect.ImmutableList;
  
  import java.lang.instrument.Instrumentation;
  
  /**
   * A utility that can be used to measure the memory footprint of an arbitrary
   * object graph. In a nutshell, the user gives a root object, and this class
   * recursively and reflectively explores the object's references.
   *
   * <p>This class can only be used if the containing jar has been given to the
   * Java VM as an agent, as follows:
   * {@code -javaagent:path/to/object-explorer.jar}
   *
   * @see #measureBytes(Object)
   * @see #measureBytes(Object, Predicate)
   */
  public class MemoryMeasurer {
    private static final Instrumentation instrumentation =
      InstrumentationGrabber.instrumentation();
  
    /*
     * The bare minimum memory footprint of an enum value, measured empirically.
     * This should be subtracted for any enum value encountered, since it
     * is static in nature.
     */
    private static final long costOfBareEnumConstant =
      instrumentation.getObjectSize(DummyEnum.CONSTANT);
  
    private enum DummyEnum {
      CONSTANT;
    }
  
    /**
     * Measures the memory footprint, in bytes, of an object graph. The object
     * graph is defined by a root object and whatever object can be reached
     * through that, excluding static fields, {@code Class} objects, and
     * fields defined in {@code enum}s (all these are considered shared values,
     * which should not contribute to the cost of any single object graph).
     *
     * <p>Equivalent to {@code measureBytes(rootObject,
     * Predicates.alwaysTrue())}.
     *
     * @param rootObject the root object that defines the object graph to be
     * measured
     * @return the memory footprint, in bytes, of the object graph
     */
    public static long measureBytes(Object rootObject) {
      return measureBytes(rootObject, Predicates.alwaysTrue());
    }
  
    /**
     * Measures the memory footprint, in bytes, of an object graph. The object
     * graph is defined by a root object and whatever object can be reached
     * through that, excluding static fields, {@code Class} objects, and
     * fields defined in {@code enum}s (all these are considered shared values,
     * which should not contribute to the cost of any single object graph), and
     * any object for which the user-provided predicate returns {@code false}.
     *
     * @param rootObject the root object that defines the object graph to be
     * measured
     * @param objectAcceptor a predicate that returns {@code true} for objects
     * to be explored (and treated as part of the object graph), or
     * {@code false} to forbid the traversal to traverse the given object
     * @return the memory footprint, in bytes, of the object graph
     */
    public static long measureBytes(Object rootObject, Predicate<Object> objectAcceptor) {
      Preconditions.checkNotNull(objectAcceptor, "predicate");
  
      Predicate<Chain> completePredicate = Predicates.and(ImmutableList.of(
          new ObjectExplorer.AtMostOncePredicate(),
          ObjectExplorer.notEnumFieldsOrClasses,
          Predicates.compose(objectAcceptor, ObjectExplorer.chainToObject)
      ));
  
      return ObjectExplorer.exploreObject(rootObject,
          new MemoryMeasurerVisitor(completePredicate));
    }
  
    private static class MemoryMeasurerVisitor implements ObjectVisitor<Long> {
      private long memory;
      private final Predicate<Chain> predicate;
  
      MemoryMeasurerVisitor(Predicate<Chain> predicate) {
        this.predicate = predicate;
      }
  
      public Traversal visit(Chain chain) {
        if (predicate.apply(chain)) {
          Object o = chain.getValue();
          memory += instrumentation.getObjectSize(o);
          if (Enum.class.isAssignableFrom(o.getClass())) {
            memory -= costOfBareEnumConstant;
          }
          return Traversal.EXPLORE;
        }
        return Traversal.SKIP;
      }
  
      public Long result() {
        return memory;
      }
    }
  }
  