Java tutorial
/* * Copyright (C) 2012 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.caliper.memory; import com.google.caliper.memory.ObjectExplorer.Feature; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultiset; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multiset; import java.util.EnumSet; /** * A tool that can qualitatively measure the footprint * ({@literal e.g.}, number of objects, references, * primitives) of a graph structure. */ public final class ObjectGraphMeasurer { /** * The footprint of an object graph. */ public final static class Footprint { private final int objects; private final int nonNullRefs; private final int nullRefs; private final ImmutableMultiset<Class<?>> primitives; private static final ImmutableSet<Class<?>> primitiveTypes = ImmutableSet.<Class<?>>of(boolean.class, byte.class, char.class, short.class, int.class, float.class, long.class, double.class); /** * Constructs a Footprint, by specifying the number of objects, * references, and primitives (represented as a {@link Multiset}). * * @param objects the number of objects * @param nonNullRefs the number of non-null references * @param nullRefs the number of null references * @param primitives the number of primitives (represented by the * respective primitive classes, e.g. {@code int.class} etc) */ public Footprint(int objects, int nonNullRefs, int nullRefs, Multiset<Class<?>> primitives) { Preconditions.checkArgument(objects >= 0, "Negative number of objects"); Preconditions.checkArgument(nonNullRefs >= 0, "Negative number of references"); Preconditions.checkArgument(nullRefs >= 0, "Negative number of references"); Preconditions.checkArgument(primitiveTypes.containsAll(primitives.elementSet()), "Unexpected primitive type"); this.objects = objects; this.nonNullRefs = nonNullRefs; this.nullRefs = nullRefs; this.primitives = ImmutableMultiset.copyOf(primitives); } /** * Returns the number of objects of this footprint. */ public int getObjects() { return objects; } /** * Returns the number of non-null references of this footprint. */ public int getNonNullReferences() { return nonNullRefs; } /** * Returns the number of null references of this footprint. */ public int getNullReferences() { return nullRefs; } /** * Returns the number of all references (null and non-null) of this footprint. */ public int getAllReferences() { return nonNullRefs + nullRefs; } /** * Returns the number of primitives of this footprint * (represented by the respective primitive classes, * {@literal e.g.} {@code int.class} etc). */ public ImmutableMultiset<Class<?>> getPrimitives() { return primitives; } @Override public int hashCode() { return Objects.hashCode(getClass().getName(), objects, nonNullRefs, nullRefs, primitives); } @Override public boolean equals(Object o) { if (o instanceof Footprint) { Footprint that = (Footprint) o; return this.objects == that.objects && this.nonNullRefs == that.nonNullRefs && this.nullRefs == that.nullRefs && this.primitives.equals(that.primitives); } return false; } @Override public String toString() { return Objects.toStringHelper(this).add("Objects", objects).add("NonNullRefs", nonNullRefs) .add("NullRefs", nullRefs).add("Primitives", primitives).toString(); } } /** * Measures the footprint of the specified 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 measure(rootObject, Predicates.alwaysTrue())}. * * @param rootObject the root object of the object graph * @return the footprint of the object graph */ public static Footprint measure(Object rootObject) { return measure(rootObject, Predicates.alwaysTrue()); } /** * Measures the footprint of the specified 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 of the object graph * @param objectAcceptor a predicate that returns {@code true} for objects * to be explored (and treated as part of the footprint), or {@code false} * to forbid the traversal to traverse the given object * @return the footprint of the object graph */ public static Footprint measure(Object rootObject, Predicate<Object> objectAcceptor) { Preconditions.checkNotNull(objectAcceptor, "predicate"); Predicate<Chain> completePredicate = Predicates.and( ImmutableList.of(ObjectExplorer.notEnumFieldsOrClasses, new ObjectExplorer.AtMostOncePredicate(), Predicates.compose(objectAcceptor, ObjectExplorer.chainToObject))); return ObjectExplorer.exploreObject(rootObject, new ObjectGraphVisitor(completePredicate), EnumSet.of(Feature.VISIT_PRIMITIVES, Feature.VISIT_NULL)); } private static class ObjectGraphVisitor implements ObjectVisitor<Footprint> { private int objects; // -1 to account for the root, which has no reference leading to it private int nonNullReferences = -1; private int nullReferences = 0; private final Multiset<Class<?>> primitives = HashMultiset.create(); private final Predicate<Chain> predicate; ObjectGraphVisitor(Predicate<Chain> predicate) { this.predicate = predicate; } @Override public Traversal visit(Chain chain) { if (chain.isPrimitive()) { primitives.add(chain.getValueType()); return Traversal.SKIP; } else { if (chain.getValue() == null) { nullReferences++; } else { nonNullReferences++; } } if (predicate.apply(chain) && chain.getValue() != null) { objects++; return Traversal.EXPLORE; } return Traversal.SKIP; } @Override public Footprint result() { return new Footprint(objects, nonNullReferences, nullReferences, ImmutableMultiset.copyOf(primitives)); } } private ObjectGraphMeasurer() { } }