net.sf.ehcache.pool.sizeof.ObjectGraphWalker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
/**
* Copyright Terracotta, 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 net.sf.ehcache.pool.sizeof;
import java.lang.ref.SoftReference;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Stack;
import net.sf.ehcache.pool.sizeof.filter.SizeOfFilter;
import net.sf.ehcache.util.WeakIdentityConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This will walk an object graph and let you execute some "function" along the way
*
* @author Alex Snaps
*/
final class ObjectGraphWalker {
private static final Logger LOG = LoggerFactory.getLogger(ObjectGraphWalker.class);
private static final String TC_INTERNAL_FIELD_PREFIX = "$__tc_";
private static final String VERBOSE_DEBUG_LOGGING = "net.sf.ehcache.sizeof.verboseDebugLogging";
private static final String CONTINUE_MESSAGE =
"The configured limit of {0} object references was reached while attempting to calculate the size of the object graph." +
" Severe performance degradation could occur if the sizing operation continues. This can be avoided by setting the CacheManger" +
" or Cache element's maxDepthExceededBehavior to \"abort\" or adding stop points with @IgnoreSizeOf annotations." +
" If performance degradation is NOT an issue at the configured limit, raise the limit value using the CacheManager or Cache" +
" element's maxDepth attribute. For more information, see the Ehcache configuration documentation.";
private static final String ABORT_MESSAGE =
"The configured limit of {0} object references was reached while attempting to calculate the size of the object graph." +
" This can be avoided by adding stop points with @IgnoreSizeOf annotations. Since the CacheManger or Cache " +
" element's maxDepthExceededBehavior is set to \"abort\", the sizing operation has stopped and the reported cache size is not" +
" accurate. If performance degradation is NOT an issue at the configured limit, raise the limit value using the CacheManager" +
" or Cache element's maxDepth attribute. For more information, see the Ehcache configuration documentation.";
private static final boolean USE_VERBOSE_DEBUG_LOGGING;
// Todo this is probably not what we want...
private final WeakIdentityConcurrentMap, SoftReference>> fieldCache =
new WeakIdentityConcurrentMap, SoftReference>>();
private final WeakIdentityConcurrentMap, Boolean> classCache =
new WeakIdentityConcurrentMap, Boolean>();
private final SizeOfFilter sizeOfFilter;
private final Visitor visitor;
static {
USE_VERBOSE_DEBUG_LOGGING = getVerboseSizeOfDebugLogging();
}
/**
* Constructor
*
* @param visitor the visitor to use
* @param filter the filtering
* @see Visitor
* @see SizeOfFilter
*/
ObjectGraphWalker(Visitor visitor, SizeOfFilter filter) {
this.visitor = visitor;
this.sizeOfFilter = filter;
}
private static boolean getVerboseSizeOfDebugLogging() {
String verboseString = System.getProperty(VERBOSE_DEBUG_LOGGING, "false").toLowerCase();
return verboseString.equals("true");
}
/**
* The visitor to execute the function on each node of the graph
* This is only to be used for the sizing of an object graph in memory!
*/
static interface Visitor {
/**
* The visit method executed on each node
*
* @param object the reference at that node
* @return a long for you to do things with...
*/
public long visit(Object object);
}
/**
* Walk the graph and call into the "visitor"
*
* @param maxDepth maximum depth to traverse the object graph
* @param abortWhenMaxDepthExceeded true if the object traversal should be aborted when the max depth is exceeded
* @param root the roots of the objects (a shared graph will only be visited once)
* @return the sum of all Visitor#visit returned values
*/
long walk(int maxDepth, boolean abortWhenMaxDepthExceeded, Object... root) {
StringBuilder traversalDebugMessage = null;
long result = 0;
boolean warned = false;
try {
Stack