com.google.caliper.memory.ObjectGraphMeasurer.java Source code

Java tutorial

Introduction

Here is the source code for com.google.caliper.memory.ObjectGraphMeasurer.java

Source

/*
 * 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() {
    }
}