add sizeof util
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/AbstractProfileNode.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/AbstractProfileNode.java
new file mode 100644
index 0000000..a02c4c9
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/AbstractProfileNode.java
@@ -0,0 +1,120 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+import java.io.PrintWriter;

+import java.io.StringWriter;

+import java.util.LinkedList;

+

+// ----------------------------------------------------------------------------

+/**

+ * Abstract base class for all node implementations in this package. 

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+abstract class AbstractProfileNode implements IObjectProfileNode, Comparable

+{

+    // public: ................................................................

+    

+    // IObjectProfileNode:

+    

+    public final int size ()

+    {

+        return m_size;

+    }

+

+    public final IObjectProfileNode parent ()

+    {

+        return m_parent;

+    }

+    

+    public final IObjectProfileNode [] path ()

+    {

+        IObjectProfileNode [] path = m_path;

+        if (path != null)

+            return path;

+        else

+        {                

+            final LinkedList /* IObjectProfileNode */ _path = new LinkedList ();

+            for (IObjectProfileNode node = this; node != null; node = node.parent ())

+                _path.addFirst (node);

+            

+            path = new IObjectProfileNode [_path.size ()];

+            _path.toArray (path);

+            

+            m_path = path; 

+            return path;

+        }

+    }

+    

+    public final IObjectProfileNode root ()

+    {

+        IObjectProfileNode node = this;

+        for (IObjectProfileNode parent = parent (); parent != null;

+             node = parent, parent = parent.parent ());

+        

+        return node;

+    }

+    

+    public final int pathlength ()

+    {

+        final IObjectProfileNode [] path = m_path;

+        if (path != null)

+            return path.length;

+        else

+        {                

+            int result = 0;

+            for (IObjectProfileNode node = this; node != null; node = node.parent ())

+                ++ result;

+                

+            return result;

+        }

+    }

+    

+    public final String dump ()

+    {

+        final StringWriter sw = new StringWriter ();

+        final PrintWriter out = new PrintWriter (sw);

+        

+        final INodeVisitor visitor =

+            ObjectProfileVisitors.newDefaultNodePrinter (out, null, null, ObjectProfiler.SHORT_TYPE_NAMES);

+        traverse (null, visitor);

+        

+        out.flush ();

+        return sw.toString ();

+    }

+    

+    // Comparable:

+    

+    public final int compareTo (final Object obj)

+    {

+        return ((AbstractProfileNode) obj).m_size - m_size;

+    }

+    

+    public String toString ()

+    {

+        return super.toString () + ": name = " + name () + ", size = " + size ();

+    }

+    

+    // protected: .............................................................

+    

+    // package: ...............................................................

+    

+    

+    AbstractProfileNode (final IObjectProfileNode parent)

+    {

+        m_parent = parent;

+    }

+    

+

+    int m_size;

+    

+    static final IObjectProfileNode [] EMPTY_OBJECTPROFILENODE_ARRAY = new IObjectProfileNode [0];

+    

+    // private: ...............................................................

+    

+    

+    private final IObjectProfileNode m_parent;

+    private transient IObjectProfileNode [] m_path;

+

+} // end of class

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/AbstractShellProfileNode.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/AbstractShellProfileNode.java
new file mode 100644
index 0000000..0e9d662
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/AbstractShellProfileNode.java
@@ -0,0 +1,61 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+// ----------------------------------------------------------------------------

+/**

+ * Abstract base class for all shell pseudo-node implementations in this package.

+ * It is used primarily to lower memory consumption by shell nodes.

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+abstract class AbstractShellProfileNode extends AbstractProfileNode

+{

+    // public: ................................................................    

+

+    public final Object object ()

+    {

+        return null;

+    }

+    

+    public final IObjectProfileNode shell ()

+    {

+        return null;

+    }

+    

+    public final IObjectProfileNode [] children ()

+    {

+        return EMPTY_OBJECTPROFILENODE_ARRAY;

+    }

+    

+    public final int refcount ()

+    {

+        return 0;

+    }

+    

+    public final boolean traverse (final INodeFilter filter, final INodeVisitor visitor)

+    {

+        if ((visitor != null) && ((filter == null) || filter.accept (this)))

+        {

+            visitor.previsit (this);

+            visitor.postvisit (this);

+            

+            return true;

+        }

+        

+        return false;

+    }

+    

+    // protected: .............................................................

+

+    // package: ...............................................................

+    

+    

+    AbstractShellProfileNode (final IObjectProfileNode parent)

+    {

+        super (parent);

+    }

+    

+    // private: ...............................................................

+

+} // end of class

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ArrayIndexLink.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ArrayIndexLink.java
new file mode 100644
index 0000000..74c0389
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ArrayIndexLink.java
@@ -0,0 +1,55 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+// ----------------------------------------------------------------------------

+/**

+ * An {@link ILink} implementation for tree links created by array fields.

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+final class ArrayIndexLink implements ILink

+{

+    // public: ................................................................

+    

+    // ILink:

+    

+    public String name ()

+    {

+        final StringBuffer s = new StringBuffer ();

+        

+        ILink l = this;

+        while (l instanceof ArrayIndexLink)

+        {

+            final ArrayIndexLink asl = (ArrayIndexLink) l; 

+            

+            s.insert (0, ']');

+            s.insert (0, asl.m_index);

+            s.insert (0, '[');

+            

+            l = asl.m_container;

+        }

+        

+        s.insert (0, l != null ? l.name () : ObjectProfiler.INPUT_OBJECT_NAME);

+        

+        return s.toString ();

+    }

+    

+    // protected: .............................................................

+

+    // package: ...............................................................

+    

+    

+    ArrayIndexLink (final ILink container, final int index)

+    {

+        m_container = container;

+        m_index = index;

+    } 

+    

+    // private: ...............................................................

+    

+    

+    private final ILink m_container;

+    private final int m_index;

+

+} // end of class

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ArrayShellProfileNode.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ArrayShellProfileNode.java
new file mode 100644
index 0000000..3b14679
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ArrayShellProfileNode.java
@@ -0,0 +1,39 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+// ----------------------------------------------------------------------------

+/**

+ * A shell pseudo-node implementation for an array class. 

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+final class ArrayShellProfileNode extends AbstractShellProfileNode

+{

+    // public: ................................................................

+    

+    public String name ()

+    {

+        return "<shell: " + ObjectProfiler.typeName (m_type, ObjectProfiler.SHORT_TYPE_NAMES) + ", length=" + m_length + ">";

+    }

+    

+    // protected: .............................................................

+

+    // package: ...............................................................

+    

+    

+    ArrayShellProfileNode (final IObjectProfileNode parent, final Class type, final int length)

+    {

+        super (parent);

+        

+        m_type = type;

+        m_length = length;

+    }

+    

+    // private: ...............................................................

+

+

+    private final Class m_type;

+    private final int m_length;

+    

+} // end of class

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ClassFieldLink.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ClassFieldLink.java
new file mode 100644
index 0000000..de76e16
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ClassFieldLink.java
@@ -0,0 +1,39 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+import java.lang.reflect.Field;

+

+// ----------------------------------------------------------------------------

+/**

+ * An {@link ILink} implementation for tree links created by class instance fields.

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+final class ClassFieldLink implements ILink

+{

+    // public: ................................................................

+    

+    // ILink:

+    

+    public String name ()

+    {

+        return ObjectProfiler.fieldName (m_field, ObjectProfiler.SHORT_TYPE_NAMES);

+    }

+    

+    // protected: .............................................................

+

+    // package: ...............................................................

+    

+    

+    ClassFieldLink (final Field field)

+    {

+        m_field = field;

+    }

+    

+    // private: ...............................................................

+    

+    

+    private final Field m_field;

+

+} // end of class

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ILink.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ILink.java
new file mode 100644
index 0000000..28fb1bc
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ILink.java
@@ -0,0 +1,23 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+// ----------------------------------------------------------------------------

+/**

+ * Interface used internally for memory-efficient representations of names

+ * of profile tree links between profile tree nodes.

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+interface ILink

+{

+    // public: ................................................................

+    

+    /**

+     * Returns the string that will be used for a {@link IObjectProfileNode#name()}

+     * implementation. It is expected that the implementation will generate

+     * the return on every call to this method and not keep in memory. 

+     */

+    String name ();

+    

+} // end of interface

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/IObjectProfileNode.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/IObjectProfileNode.java
new file mode 100644
index 0000000..5ed7807
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/IObjectProfileNode.java
@@ -0,0 +1,179 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+// ----------------------------------------------------------------------------

+/**

+ * The main interface for exploring an object profile tree. See individual methods

+ * for details.

+ *  

+ * @see ObjectProfiler#profile(Object)

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+public

+interface IObjectProfileNode

+{

+    // public: ................................................................

+    

+    /**

+     * A generic interface for defining node filters. A node filter is used

+     * as a guard to determine whether a given visitor should be given a shot

+     * as doing something with a profile tree node.

+     */

+    interface INodeFilter

+    {

+        /**

+         * @param node about to be visited [never null]

+         * @return 'true' if 'node' and its children should be visited

+         */

+        boolean accept (IObjectProfileNode node);

+        

+    } // end of nested interface

+

+    /**

+     * A generic interface for defining node visitors. A node visitor is applied

+     * to a profile tree node both before and after visiting the node's children,

+     * if any.

+     */

+    interface INodeVisitor

+    {

+        /**

+         * Pre-order visit.

+         * 

+         * @param node being visited [never null]

+         */

+        void previsit (IObjectProfileNode node);

+        

+        /**

+         * Post-order visit.

+         * 

+         * @param node being visited [never null]

+         */

+        void postvisit (IObjectProfileNode node);

+        

+    } // end of nested interface

+

+    

+    /**

+     * Returns the object associated with this node. This is never null except

+     * for shell pseudo-nodes.

+     * 

+     * @return object instance [null only for shell nodes]

+     */

+    Object object ();

+    

+    /**

+     * Returns a human-readable name for this node, usually derived from the

+     * class field or array index that was used to reach the {@link #object() object}

+     * associated with this node.

+     * 

+     * @return node name [never null]

+     */

+    String name ();

+    

+    /**

+     * Returns the full size (in bytes) assigned to this node in its profile

+     * tree. This is the sum of sizes of the node class's {@link #shell() shell}

+     * and its non-primitive non-null {@link #children() instance fields},

+     * computed as a closure over the spanning tree produced by {@link ObjectProfiler#profile}. 

+     * 

+     * @return total node size [always positive]

+     */

+    int size ();

+    /**

+     * Returns the reference count for the associated {@link #object()}. This is

+     * exactly the number of unique references to this object in the object

+     * graph submitted to {@link ObjectProfiler#profile}.

+     * 

+     * @return reference count [always positive]

+     */

+    int refcount ();

+    

+    /**

+     * Returns the assigned ownership parent for this node. This is null for the

+     * root node.

+     * 

+     * @return parent node [null only for the root node]

+     */

+    IObjectProfileNode parent ();

+    /**

+     * Returns all children of this node. These are non-null references found

+     * in this object's class fields (or array slots if the object is of an

+     * array type). The result is sorted in decreasing {@link #size() size} order.<P>

+     * 

+     * Note: the returned array also contains the {@link #shell() shell} pseudo-node.

+     *  

+     * @return array of children nodes, sorted by size [never null, may be empty]

+     */

+    IObjectProfileNode [] children ();

+    /**

+     * Returns the shell pseudo-node for this node. This represents all instance

+     * data fields that are "inlined" in the class definition represented by

+     * this node (including all superclasses all the way to java.lang.Object).

+     * This includes primitive data fields, object references representing

+     * non-primitive fields, and (for arrays) the array length field and storage

+     * required for the array slots.<P>

+     * 

+     * Another way to describe this is that node.shell().size() is the minimum

+     * size an instance of node.object().getClass() can be (when all non-primitive

+     * instance fields are set to 'null').<P>

+     * 

+     * The returned reference is also guaranteed to be present somewhere in

+     * the array returned by {@link #children()}. This data is kept in a

+     * separate node instance to simplify tree visiting and node filtering. 

+     * 

+     * @return shell pseudo-node [null only for shell nodes]

+     */

+    IObjectProfileNode shell ();

+    

+    /**

+     * Returns the full path from the profile tree root to this node, in that

+     * direction. The result includes the root node as well as the current node.<P>

+     * 

+     * Invariant: node.root() == node.path()[0]

+     * Invariant: node.path()[node.path().length - 1] == node

+     * Invariant: node.path().length == node.pathlength()

+     * 

+     * @return node tree path [never null/empty]

+     */

+    IObjectProfileNode [] path ();

+    /**

+     * A convenience method for retrieving the root node from any node in a profile

+     * tree.<P>

+     * 

+     * Invariant: node.root() == node iff 'node' is the root of its profile tree

+     * Invariant: node.root() == node.path()[0]

+     * 

+     * @return the root node for the profile tree that the current node is

+     * a part of [never null]

+     */

+    IObjectProfileNode root ();

+    /**

+     * A convenience method for retrieving this node's tree path length.

+     * 

+     * @return path length [always positive]

+     */

+    int pathlength ();

+    

+    /**

+     * A generic hook for traversing profile trees using {@link INodeFilter filters}

+     * and {@link INodeVisitor visitors}. See IObjectProfileNode.INodeFilter and

+     * IObjectProfileNode.INodeVisitor for more details

+     * 

+     * @param filter [null is equivalent to no filtering]

+     * @param visitor [may not be null]

+     * 

+     * @return 'true' iff either 'filter' was null or it returned 'true' for this node

+     */

+    boolean traverse (INodeFilter filter, INodeVisitor visitor);

+    

+    /**

+     * Dumps this node into a flat-text format used by the {@link ObjectProfileVisitors#newDefaultNodePrinter()}

+     * default node visitor.

+     * 

+     * @return indented dump string [could be very large]

+     */

+    String dump ();

+

+} // end of interface

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfileFilters.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfileFilters.java
new file mode 100644
index 0000000..ff1d6e7
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfileFilters.java
@@ -0,0 +1,166 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+// ----------------------------------------------------------------------------

+/**

+ * A Factory for a few stock node filters. See the implementation for details.

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+public

+abstract class ObjectProfileFilters

+{

+    // public: ................................................................

+    

+    /**

+     * Factory method for creating a visitor that only accepts profile nodes

+     * with sizes larger than a given threshold value.

+     * 

+     * @param threshold node size in bytes

+     */

+    public static ObjectProfileNode.INodeFilter newSizeFilter (final int threshold)

+    {

+        return new SizeFilter (threshold);

+    }

+    

+    /**

+     * Factory method for creating a visitor that accepts a profile node only if

+     * it is at least the k-th largest child of its parent for a given value

+     * of k. E.g., newRankFilter(1) will prune the profile tree so that only the

+     * largest child is visited for every node. 

+     * 

+     * @param rank acceptable size rank [must be >= 0]

+     */

+    public static ObjectProfileNode.INodeFilter newRankFilter (final int rank)

+    {

+        return new RankFilter (rank);

+    }

+    

+    /**

+     * Factory method for creating a visitor that accepts a profile node only if

+     * its size is larger than a given threshold relative to the size of the root

+     * node (i.e., size of the entire profile tree). 

+     * 

+     * @param threshold size fraction threshold

+     */

+    public static ObjectProfileNode.INodeFilter newSizeFractionFilter (final double threshold)

+    {

+        return new SizeFractionFilter (threshold);

+    }

+

+    /**

+     * Factory method for creating a visitor that accepts a profile node only if

+     * its size is larger than a given threshold relative to the size of its

+     * parent node. This is useful for pruning the profile tree to show the

+     * largest contributors at every tree level.

+     * 

+     * @param threshold size fraction threshold

+     */    

+    public static ObjectProfileNode.INodeFilter newParentSizeFractionFilter (final double threshold)

+    {

+        return new ParentSizeFractionFilter (threshold);

+    }

+    

+    // protected: .............................................................

+

+    // package: ...............................................................

+    

+    // private: ...............................................................

+    

+    

+    private ObjectProfileFilters () {} // this class is not extendible

+

+

+    private static final class SizeFilter implements IObjectProfileNode.INodeFilter

+    {

+        public boolean accept (final IObjectProfileNode node)

+        {

+            return node.size () >= m_threshold;

+        }

+        

+        

+        SizeFilter (final int threshold)

+        {

+            m_threshold = threshold;

+        }

+        

+        

+        private final int m_threshold;

+

+    } // end of nested class

+    

+    

+    private static final class RankFilter implements IObjectProfileNode.INodeFilter

+    {

+        public boolean accept (final IObjectProfileNode node)

+        {

+            final IObjectProfileNode parent = node.parent ();

+            if (parent == null) return true;

+            

+            final IObjectProfileNode [] siblings = parent.children ();

+            for (int r = 0, rLimit = Math.min (siblings.length, m_threshold); r< rLimit; ++ r)

+                if (siblings [r] == node) return true;

+            

+            return false;

+        }

+        

+        

+        RankFilter (final int threshold)

+        {

+            m_threshold = threshold;

+        }

+        

+        

+        private final int m_threshold;

+

+    } // end of nested class

+    

+    

+    private static final class SizeFractionFilter implements IObjectProfileNode.INodeFilter

+    {

+        public boolean accept (final IObjectProfileNode node)

+        {

+            if (node.size () >= m_threshold * node.root ().size ())

+                return true;

+            else

+                return false;

+        }

+        

+        

+        SizeFractionFilter (final double threshold)

+        {

+            m_threshold = threshold;

+        }

+        

+        

+        private final double m_threshold;

+

+    } // end of nested class

+    

+    

+    private static final class ParentSizeFractionFilter implements IObjectProfileNode.INodeFilter

+    {

+        public boolean accept (final IObjectProfileNode node)

+        {

+            final IObjectProfileNode parent = node.parent ();

+            if (parent == null)

+                return true; // always accept root node

+            else if (node.size () >= m_threshold * parent.size ())

+                return true;

+            else

+                return false;

+        }

+        

+        

+        ParentSizeFractionFilter (final double threshold)

+        {

+            m_threshold = threshold;

+        }

+        

+        

+        private final double m_threshold;

+

+    } // end of nested class

+

+} // end of class

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfileNode.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfileNode.java
new file mode 100644
index 0000000..33c9bd2
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfileNode.java
@@ -0,0 +1,134 @@
+
+package com.supwisdom.tutorial.sizeof;
+
+import java.util.Arrays;
+   

+// ----------------------------------------------------------------------------
+/**
+ * A non-shell profile tree node implementation. This implementation trades off
+ * some object orientation "niceness" to achieve more memory compactness.
+ * 
+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003
+ */
+final class ObjectProfileNode extends AbstractProfileNode
+{

+    // public: ................................................................    
+
+    public Object object ()
+    {
+        return m_obj;
+    }
+    
+    public String name ()
+    {
+        return m_link == null ? ObjectProfiler.INPUT_OBJECT_NAME : m_link.name ();
+    }
+
+    public IObjectProfileNode shell ()

+    {

+        return m_shell;

+    }

+    

+    public IObjectProfileNode [] children ()

+    {

+        return m_children;

+    }

+

+    public int refcount ()

+    {

+        return m_refcount;

+    }

+    
+    public boolean traverse (final INodeFilter filter, final INodeVisitor visitor)
+    {
+        if ((visitor != null) && ((filter == null) || filter.accept (this)))
+        {
+            visitor.previsit (this);
+            
+            final IObjectProfileNode [] children = m_children;
+            for (int i = 0; i < children.length; ++ i)
+            {
+                children [i].traverse (filter, visitor);
+            }
+            
+            visitor.postvisit (this);
+            
+            return true;
+        }
+        
+        return false;
+    }
+            

+    // protected: .............................................................
+
+    // package: ...............................................................
+    
+    /*
+     * This method manages the vector in m_children field for an unfinished node.
+     */
+    void addFieldRef (final IObjectProfileNode node)
+    {
+        // [m_size is the child count]
+        
+        IObjectProfileNode [] children = m_children;
+        final int childrenLength = children.length;
+        if (m_size >= childrenLength)
+        {
+            final IObjectProfileNode [] newchildren = new IObjectProfileNode [Math.max (1, childrenLength << 1)];
+            System.arraycopy (children, 0, newchildren, 0, childrenLength);
+            m_children = children = newchildren;
+        }
+        children [m_size ++] = node;
+    }
+    
+    /*
+     * This method is called once on every node to lock it down into its
+     * immutable and most compact representation during phase 2 of profile
+     * tree construction.
+     */
+    void finish ()
+    {
+        final int childCount = m_size; // m_size is the child count for a non-shell node
+        if (childCount > 0)
+        {
+            if (childCount < m_children.length)
+            {
+                final IObjectProfileNode [] newadj = new IObjectProfileNode [childCount];
+                System.arraycopy (m_children, 0, newadj, 0, childCount);
+                
+                m_children = newadj;
+            }
+            
+            Arrays.sort (m_children);
+            
+            int size = 0;
+            for (int i = 0; i < childCount; ++ i)
+            {
+                size += m_children [i].size ();
+            }
+            m_size = size; // m_size is the full node size for all nodes
+        }
+    }
+    
+    

+    ObjectProfileNode (final ObjectProfileNode parent, final Object obj, final ILink link)

+    {
+        super (parent);
+
+        m_obj = obj;
+        m_link = link;

+        m_refcount = 1;
+        m_children = EMPTY_OBJECTPROFILENODE_ARRAY;
+    }
+
+
+    final ILink m_link;
+    final Object m_obj;
+    int m_refcount;
+    AbstractShellProfileNode m_shell;
+    IObjectProfileNode [] m_children;
+        
+    // private: ...............................................................
+
+} // end of class
+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfileVisitors.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfileVisitors.java
new file mode 100644
index 0000000..35daf78
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfileVisitors.java
@@ -0,0 +1,298 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+import java.io.OutputStream;

+import java.io.OutputStreamWriter;

+import java.io.PrintWriter;

+import java.io.UnsupportedEncodingException;

+import java.text.DecimalFormat;

+import java.text.NumberFormat;

+

+// ----------------------------------------------------------------------------

+/**

+ * A Factory for a few stock node visitors. See the implementation for details.

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+public

+abstract class ObjectProfileVisitors

+{

+    // public: ................................................................

+    

+    /**

+     * Factory method for creating the default plain text node node print visitor.

+     * It is up to the caller to buffer 'out'.

+     * 

+     * @param out writer to dump the nodes into [may not be null]

+     * @param indent indent increment string [null is equivalent to "  "]

+     * @param format percentage formatter to use [null is equivalent to

+     * NumberFormat.getPercentInstance (), with a single fraction digit]

+     * @param shortClassNames 'true' causes all class names to be dumped in

+     * compact [no package prefix] form

+     */

+    public static ObjectProfileNode.INodeVisitor

+                                    newDefaultNodePrinter (final PrintWriter out,

+                                                           final String indent,

+                                                           final DecimalFormat format,

+                                                           final boolean shortClassNames)

+    {

+        return new DefaultNodePrinter (out, indent, format, shortClassNames);

+    }

+    

+    /**

+     * Factory method for creating the XML output visitor. To create a valid

+     * XML document, start the traversal on the profile root node. It is up to

+     * the caller to buffer 'out'.

+     * 

+     * @param out stream to dump the nodes into [may not be null]

+     * @param indent indent increment string [null is equivalent to "  "]

+     * @param format percentage formatter to use [null is equivalent to

+     * NumberFormat.getPercentInstance (), with a single fraction digit]

+     * @param shortClassNames 'true' causes all class names to be dumped in

+     * compact [no package prefix] form

+     */

+    public static ObjectProfileNode.INodeVisitor

+                                    newXMLNodePrinter (final OutputStream out,

+                                                       final String indent,

+                                                       final DecimalFormat format,

+                                                       final boolean shortClassNames)

+    {

+        return new XMLNodePrinter (out, indent, format, shortClassNames);

+    }

+    

+    // protected: .............................................................

+

+    // package: ...............................................................

+    

+    // private: ...............................................................

+    

+    

+    private ObjectProfileVisitors () {} // this class is not extendible

+

+

+    private static abstract class AbstractProfileNodeVisitor

+                            implements IObjectProfileNode.INodeVisitor

+    {

+        public void previsit (final IObjectProfileNode node)

+        {

+        }

+        

+        public void postvisit (final IObjectProfileNode node)

+        {

+        }

+        

+    } // end of nested class

+    

+ 

+    /**

+     * This visitor prints out a node in plain text format. The output is

+     * indented according to the length of the node's path within its

+     * profile tree.

+     */

+    private static final class DefaultNodePrinter extends AbstractProfileNodeVisitor

+    {

+        public void previsit (final IObjectProfileNode node)

+        {

+            final StringBuffer sb = new StringBuffer ();

+            

+            for (int p = 0, pLimit = node.pathlength (); p < pLimit; ++ p)

+                sb.append (m_indent);

+            

+            final IObjectProfileNode root = node.root ();

+            

+            sb.append (node.size ());

+            if (node != root) // root node is always 100% of the overall size

+            {

+                sb.append (" (");

+                sb.append (m_format.format ((double) node.size () / root.size ()));

+                sb.append (")");

+            }

+            sb.append (" -> ");

+            sb.append (node.name ());

+            if (node.object () != null) // skip shell pseudo-nodes

+            {

+                sb.append (" : ");

+                sb.append (ObjectProfiler.typeName (node.object ().getClass (), m_shortClassNames));

+                

+                if (node.refcount () > 1) // show refcount only when it's > 1

+                {

+                    sb.append (", refcount=");

+                    sb.append (node.refcount ());

+                }

+            }

+            

+            m_out.println (sb);

+            m_out.flush ();

+        }

+        

+        

+        DefaultNodePrinter (final PrintWriter out, final String indent, final DecimalFormat format, final boolean shortClassNames)

+        {

+            assert out != null : "null input: out";

+            

+            m_out = out;

+            m_indent = indent != null ? indent : "  ";

+            

+            if (format != null)

+                m_format = format;

+            else

+            {

+                m_format = (DecimalFormat) NumberFormat.getPercentInstance ();

+                m_format.setMaximumFractionDigits (1);

+            }

+            

+            m_shortClassNames = shortClassNames;

+        }

+        

+        

+        private final PrintWriter m_out;

+        private final String m_indent;

+        private final DecimalFormat m_format;

+        private final boolean m_shortClassNames;

+        

+    } // end of nested class

+    

+    

+    /*

+     * This visitor can dump a profile tree in an XML file, which can be handy

+     * for examination of very large object graphs.

+     */

+    private static final class XMLNodePrinter extends AbstractProfileNodeVisitor

+    {

+        public void previsit (final IObjectProfileNode node)

+        {

+            final IObjectProfileNode root = node.root ();

+            final boolean isRoot = root == node;

+            

+            if (isRoot)

+            {

+                m_out.println ("<?xml version=\"1.0\" encoding=\"" + ENCODING + "\"?>");

+                m_out.println ("<input>"); 

+            }

+            

+            final StringBuffer indent = new StringBuffer ();

+            for (int p = 0, pLimit = node.pathlength (); p < pLimit; ++ p)

+                indent.append (m_indent);

+            

+            final StringBuffer sb = new StringBuffer ();

+            sb.append ("<object");

+            

+            sb.append  (" size=\"");

+            sb.append (node.size ());

+            sb.append ('\"');

+            

+            if (! isRoot)

+            {

+                sb.append (" part=\"");

+                sb.append (m_format.format ((double) node.size () / root.size ()));

+                sb.append ('\"');

+            }

+            

+            sb.append  (" name=\"");

+            XMLEscape (node.name (), sb);

+            sb.append ('\"');

+            

+            if (node.object () != null) // skip shell pseudo-nodes

+            {

+                sb.append  (" objclass=\"");

+                

+                XMLEscape (ObjectProfiler.typeName (node.object ().getClass (), m_shortClassNames), sb);

+                sb.append ('\"');

+                

+                if (node.refcount () > 1)

+                {

+                    sb.append  (" refcount=\"");

+                    sb.append (node.refcount ());

+                    sb.append ('\"');

+                }

+            }

+            

+            sb.append ('>');

+            m_out.print (indent); m_out.println (sb);

+        }

+        

+        public void postvisit (final IObjectProfileNode node)

+        {

+            final StringBuffer indent = new StringBuffer ();

+            for (int p = 0, pLimit = node.pathlength (); p < pLimit; ++ p)

+                indent.append (m_indent);

+            

+            m_out.print (indent); m_out.println ("</object>");

+            if (node.root () == node)

+            {

+                m_out.println ("</input>");

+                m_out.flush (); 

+            }

+        }

+        

+        

+        XMLNodePrinter (final OutputStream out, final String indent, final DecimalFormat format, final boolean shortClassNames)

+        {

+            assert out != null : "null input: out";

+            

+            try

+            {

+                m_out = new PrintWriter (new OutputStreamWriter (out, ENCODING));

+            }

+            catch (UnsupportedEncodingException uee)

+            {

+                throw new Error (uee);

+            }

+            

+            m_indent = indent != null ? indent : "  ";

+            

+            if (format != null)

+                m_format = format;

+            else

+            {

+                m_format = (DecimalFormat) NumberFormat.getPercentInstance ();

+                m_format.setMaximumFractionDigits (2);

+            }

+            

+            m_shortClassNames = shortClassNames;

+        }

+        

+        private static void XMLEscape (final String s, final StringBuffer append)

+        {

+            final char [] chars = s.toCharArray (); 

+            

+            for (int i = 0, iLimit = s.length (); i < iLimit; ++ i)

+            {

+                final char c = chars [i];

+                switch (c)

+                {

+                    case '<':

+                        append.append ("&lt;");

+                        break;

+                        

+                    case '>':

+                        append.append ("&gt;");

+                        break;

+    

+                    case '"':

+                        append.append ("&#34;");

+                        break;

+                        

+                    case '&':

+                        append.append ("&amp;");

+                        break;

+                    

+                    default:

+                        append.append (c);    

+                    

+                } // end of switch

+            }

+        }

+        

+        

+        private final PrintWriter m_out;

+        private final String m_indent;

+        private final DecimalFormat m_format;

+        private final boolean m_shortClassNames;

+        

+        private static final String ENCODING = "UTF-8";

+        

+    } // end of nested class

+

+} // end of class

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfiler.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfiler.java
new file mode 100644
index 0000000..9f32b71
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectProfiler.java
@@ -0,0 +1,619 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+import java.lang.reflect.Array;

+import java.lang.reflect.Field;

+import java.lang.reflect.Modifier;

+import java.security.AccessController;

+import java.security.PrivilegedActionException;

+import java.security.PrivilegedExceptionAction;

+import java.util.Arrays;

+import java.util.IdentityHashMap;

+import java.util.LinkedList;

+import java.util.List;

+import java.util.Map;

+import java.util.WeakHashMap;

+

+// ----------------------------------------------------------------------------

+/**

+ * This non-instantiable class presents an API for object sizing and profiling

+ * as described in the article. See individual methods for details.<P>

+ * 

+ * This implementation is J2SE 1.4+ only. You would need to code your own

+ * identity hashmap to port this to earlier Java versions.<P>

+ * 

+ * Security: this implementation uses AccessController.doPrivileged() so it

+ * could be granted privileges to access non-public class fields separately

+ * from your main application code. The minimum set of persmissions necessary

+ * for this class to function correctly follows:

+ * <pre>

+ *      permission java.lang.RuntimePermission "accessDeclaredMembers";

+ *      permission java.lang.reflect.ReflectPermission "suppressAccessChecks";

+ * </pre> 

+ * 

+ * @see IObjectProfileNode 

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+public

+abstract class ObjectProfiler

+{

+    // public: ................................................................

+    

+    // the following constants are physical sizes (in bytes) and are JVM-dependent:

+    // [the current values are Ok for most 32-bit JVMs]

+

+    public static final int OBJECT_SHELL_SIZE   = 8; // java.lang.Object shell size in bytes

+    public static final int OBJREF_SIZE         = 4;

+    

+    public static final int LONG_FIELD_SIZE     = 8;

+    public static final int INT_FIELD_SIZE      = 4;

+    public static final int SHORT_FIELD_SIZE    = 2;

+    public static final int CHAR_FIELD_SIZE     = 2;

+    public static final int BYTE_FIELD_SIZE     = 1;

+    public static final int BOOLEAN_FIELD_SIZE  = 1;

+    public static final int DOUBLE_FIELD_SIZE   = 8;

+    public static final int FLOAT_FIELD_SIZE    = 4;

+    

+    

+    /** Set this to 'true' to make the node names default to using

+     * class names without package prefixing for more compact dumps */

+    public static final boolean SHORT_TYPE_NAMES = false;

+    

+    /** If this is 'true', node names will use short class names

+     * for common classes in java.lang.* and java.util.*, regardless

+     * of {@link #SHORT_TYPE_NAMES} setting. */

+    public static final boolean SHORT_COMMON_TYPE_NAMES = true;

+    

+    

+    /**

+     * Estimates the full size of the object graph rooted at 'obj'.

+     * Duplicate data instances are correctly accounted for. The implementation

+     * is not recursive.<P>

+     * 

+     * Invariant: sizeof(obj) == profile(obj).size() if 'obj' is not null

+     * 

+     * @param obj input object instance to be measured

+     * @return 'obj' size [0 if 'obj' is null']

+     */

+    public static int sizeof (final Object obj)

+    {

+        if (obj == null) return 0;

+        

+        final IdentityHashMap visited = new IdentityHashMap ();

+

+        return computeSizeof (obj, visited, CLASS_METADATA_CACHE);

+    }

+    

+    /**

+     * Estimates the full size of the object graph rooted at 'obj' by

+     * pre-populating the "visited" set with the object graph rooted

+     * at 'base'. The net effect is to compute the size of 'obj' by summing

+     * over all instance data contained in 'obj' but not in 'base'. 

+     * 

+     * @param base graph boundary [may not be null]

+     * @param obj input object instance to be measured

+     * @return 'obj' size [0 if 'obj' is null']

+     */

+    public static int sizedelta (final Object base, final Object obj)

+    {

+        if (obj == null) return 0;

+        if (base == null) throw new IllegalArgumentException ("null input: base");

+        

+        final IdentityHashMap visited = new IdentityHashMap ();

+        

+        computeSizeof (base, visited, CLASS_METADATA_CACHE);        

+        return visited.containsKey (obj) ? 0 : computeSizeof (obj, visited, CLASS_METADATA_CACHE);

+    }

+    

+    /**

+     * Creates a spanning tree representation for instance data contained in

+     * 'obj'. The tree is produced using bread-first traversal over the full

+     * object graph implied by non-null instance and array references originating

+     * in 'obj'.  

+     * 

+     * @see IObjectProfileNode 

+     * 

+     * @param obj input object instance to be profiled [may not be null]

+     * @return the profile tree root node [never null]

+     */

+    public static IObjectProfileNode profile (final Object obj)

+    {

+        if (obj == null) throw new IllegalArgumentException ("null input: obj");

+        

+        final IdentityHashMap visited = new IdentityHashMap (); 

+        

+        final ObjectProfileNode root = createProfileTree (obj, visited, CLASS_METADATA_CACHE);        

+        finishProfileTree (root);

+        

+        return root;  

+    }

+    

+    

+    // convenience methods:

+

+    public static String pathName (final IObjectProfileNode [] path)

+    {

+        final StringBuffer s = new StringBuffer ();

+        

+        for (int i = 0; i < path.length; ++ i)

+        {

+            if (i != 0) s.append ('/'); 

+            s.append (path [i].name ());

+        }

+        

+        return s.toString ();

+    }

+    

+    public static String fieldName (final Field field, final boolean shortClassNames)

+    {

+        return typeName (field.getDeclaringClass (), shortClassNames).concat ("#").concat (field.getName ());

+    }

+    

+    public static String typeName (Class cls, final boolean shortClassNames)

+    {

+        int dims = 0; 

+        for ( ; cls.isArray (); ++ dims) cls = cls.getComponentType ();

+        

+        String clsName = cls.getName ();

+        

+        if (shortClassNames)

+        {

+            final int lastDot = clsName.lastIndexOf ('.');

+            if (lastDot >= 0) clsName = clsName.substring (lastDot + 1);

+        }

+        else if (SHORT_COMMON_TYPE_NAMES)

+        {

+            if (clsName.startsWith ("java.lang.")) clsName = clsName.substring (10);

+            else if (clsName.startsWith ("java.util.")) clsName = clsName.substring (10);

+        }

+        

+        for (int i = 0; i < dims; ++ i) clsName = clsName.concat ("[]");

+        

+        return clsName;

+    }

+    

+    // protected: .............................................................

+

+    // package: ...............................................................

+    

+    

+    static final String INPUT_OBJECT_NAME = "<INPUT>"; // root node name

+    

+    // private: ...............................................................

+    

+    

+    /*

+     * Internal class used to cache class metadata information.

+     */

+    private static final class ClassMetadata

+    {

+        ClassMetadata (final int primitiveFieldCount,

+                       final int shellSize,

+                       final Field [] refFields)

+        {

+            m_primitiveFieldCount = primitiveFieldCount;

+            m_shellSize = shellSize;

+            m_refFields = refFields;

+        }

+        

+        // all fields are inclusive of superclasses:

+        

+        final int m_primitiveFieldCount;

+        final int m_shellSize; // class shell size

+        final Field [] m_refFields; // cached non-static fields (made accessible)

+    

+    } // end of nested class

+    

+    

+    private static final class ClassAccessPrivilegedAction

+                         implements PrivilegedExceptionAction

+    {

+        public Object run () throws Exception

+        {

+            return m_cls.getDeclaredFields ();

+        }

+        

+        void setContext (final Class cls)

+        {

+            m_cls = cls;

+        }

+        

+        

+        private Class m_cls;

+        

+    } // end of nested class

+    

+    

+    private static final class FieldAccessPrivilegedAction

+                         implements PrivilegedExceptionAction

+    {

+        public Object run () throws Exception

+        {

+            m_field.setAccessible (true);

+            

+            return null;

+        }

+        

+        void setContext (final Field field)

+        {

+            m_field = field;

+        }

+        

+        

+        private Field m_field;

+        

+    } // end of nested class

+    

+

+    private ObjectProfiler () {} // this class is not extendible

+    

+    /*

+     * The main worker method for sizeof() and sizedelta().

+     */

+    private static int computeSizeof (Object obj,

+                                      final IdentityHashMap visited,

+                                      final Map /* Class->ClassMetadata */ metadataMap)

+    {

+        // this uses depth-first traversal; the exact graph traversal algorithm

+        // does not matter for computing the total size and this method could be

+        // easily adjusted to do breadth-first instead (addLast() instead of addFirst()),

+        // however, dfs/bfs require max queue length to be the length of the longest

+        // graph path/width of traversal front correspondingly, so I expect

+        // dfs to use fewer resources than bfs for most Java objects;

+        

+        if (obj == null) return 0;

+        

+        final LinkedList queue = new LinkedList ();

+        

+        visited.put (obj, obj);

+        queue.add (obj);

+        

+        int result = 0;

+        

+        final ClassAccessPrivilegedAction caAction = new ClassAccessPrivilegedAction ();

+        final FieldAccessPrivilegedAction faAction = new FieldAccessPrivilegedAction ();

+

+        while (! queue.isEmpty ())

+        {

+            obj = queue.removeFirst ();

+            final Class objClass = obj.getClass ();

+                

+            if (objClass.isArray ())

+            {           

+                final int arrayLength = Array.getLength (obj);

+                final Class componentType = objClass.getComponentType ();

+                

+                result += sizeofArrayShell (arrayLength, componentType);

+                

+                if (! componentType.isPrimitive ())

+                {

+                    // traverse each array slot:

+                    for (int i = 0; i < arrayLength; ++ i)

+                    {

+                        final Object ref = Array.get (obj, i);

+                        

+                        if ((ref != null) && ! visited.containsKey (ref))

+                        {

+                            visited.put (ref, ref);

+                            queue.addFirst (ref);

+                        }

+                    }

+                }

+            }

+            else // the object is of a non-array type

+            {

+                final ClassMetadata metadata = getClassMetadata (objClass, metadataMap, caAction, faAction);

+                final Field [] fields = metadata.m_refFields;

+                

+                result += metadata.m_shellSize;

+                           

+                // traverse all non-null ref fields:

+                

+                for (int f = 0, fLimit = fields.length; f < fLimit; ++ f)

+                {

+                    final Field field = fields [f];

+                    

+                    final Object ref;

+                    try // to get the field value: 

+                    {

+                        ref = field.get (obj);

+                    }

+                    catch (Exception e)

+                    {

+                        throw new RuntimeException ("cannot get field [" + field.getName () + "] of class [" + field.getDeclaringClass ().getName () + "]: " + e.toString ());

+                    }

+                    

+                    if ((ref != null) && ! visited.containsKey (ref))

+                    {

+                        visited.put (ref, ref);

+                        queue.addFirst (ref);

+                    }

+                }

+            }

+        }

+

+        return result;

+    }

+    

+    

+    /*

+     * Performs phase 1 of profile creation: bread-first traversal and node

+     * creation.

+     */

+    private static ObjectProfileNode createProfileTree (Object obj,

+                                                        final IdentityHashMap visited,

+                                                        final Map /* Class->ClassMetadata */ metadataMap)

+    {

+        final ObjectProfileNode root = new ObjectProfileNode (null, obj, null);

+        

+        final LinkedList queue = new LinkedList ();

+        

+        queue.addFirst (root);

+        visited.put (obj, root);

+        

+        final ClassAccessPrivilegedAction caAction = new ClassAccessPrivilegedAction ();

+        final FieldAccessPrivilegedAction faAction = new FieldAccessPrivilegedAction ();

+        

+        while (! queue.isEmpty ())

+        {

+            final ObjectProfileNode node = (ObjectProfileNode) queue.removeFirst ();

+            

+            obj = node.m_obj;

+            final Class objClass = obj.getClass ();

+            

+            if (objClass.isArray ())

+            {           

+                final int arrayLength = Array.getLength (obj);

+                final Class componentType = objClass.getComponentType ();

+                

+                // add shell pseudo-node:

+                final AbstractShellProfileNode shell = new ArrayShellProfileNode (node, objClass, arrayLength);

+                shell.m_size = sizeofArrayShell (arrayLength, componentType);

+                

+                node.m_shell = shell;

+                node.addFieldRef (shell);

+                

+                if (! componentType.isPrimitive ())

+                {

+                    // traverse each array slot:

+                    for (int i = 0; i < arrayLength; ++ i)

+                    {

+                        final Object ref = Array.get (obj, i);

+                        

+                        if (ref != null)

+                        {

+                            ObjectProfileNode child = (ObjectProfileNode) visited.get (ref);

+                            if (child != null)

+                                ++ child.m_refcount;

+                            else

+                            {

+                                child = new ObjectProfileNode (node, ref, new ArrayIndexLink (node.m_link, i));

+                                node.addFieldRef (child);

+                                

+                                queue.addLast (child);

+                                visited.put (ref, child);

+                            }

+                        }

+                    }

+                }

+            }

+            else // the object is of a non-array type

+            {

+                final ClassMetadata metadata = getClassMetadata (objClass, metadataMap, caAction, faAction);

+                final Field [] fields = metadata.m_refFields;

+                

+                // add shell pseudo-node:

+                final AbstractShellProfileNode shell = new ObjectShellProfileNode (node, metadata.m_primitiveFieldCount, metadata.m_refFields.length);

+                shell.m_size = metadata.m_shellSize;

+                

+                node.m_shell = shell;  

+                node.addFieldRef (shell);

+                

+                // traverse all non-null ref fields:

+                for (int f = 0, fLimit = fields.length; f < fLimit; ++ f)

+                {

+                    final Field field = fields [f];

+                    

+                    final Object ref;

+                    try // to get the field value: 

+                    {

+                        ref = field.get (obj);

+                    }

+                    catch (Exception e)

+                    {

+                        throw new RuntimeException ("cannot get field [" + field.getName () + "] of class [" + field.getDeclaringClass ().getName () + "]: " + e.toString ());

+                    }

+                    

+                    if (ref != null)

+                    {

+                        ObjectProfileNode child = (ObjectProfileNode) visited.get (ref);

+                        if (child != null)

+                            ++ child.m_refcount;

+                        else

+                        {

+                            child = new ObjectProfileNode (node, ref, new ClassFieldLink (field));

+                            node.addFieldRef (child);

+                            

+                            queue.addLast (child);

+                            visited.put (ref, child);

+                        }

+                    }

+                }

+            }

+        }

+        

+        return root;

+    }

+    

+

+    /*

+     * Performs phase 2 of profile creation: totalling of node sizes (via

+     * non-recursive post-order traversal of the tree created in phase 1)

+     * and 'locking down' of profile nodes into their most compact form.

+     */

+    private static void finishProfileTree (ObjectProfileNode node)

+    {

+        final LinkedList queue = new LinkedList ();

+        IObjectProfileNode lastFinished = null;

+

+        while (node != null)

+        {

+            // note that an unfinished non-shell node has its child count

+            // in m_size and m_children[0] is its shell node:

+             

+            if ((node.m_size == 1) || (lastFinished == node.m_children [1]))

+            {

+                node.finish ();

+                lastFinished = node;

+            }

+            else

+            {

+                queue.addFirst (node);

+                for (int i = 1; i < node.m_size; ++ i)

+                {

+                    final IObjectProfileNode child = node.m_children [i];

+                    queue.addFirst (child);

+                }

+            }

+            

+            if (queue.isEmpty ())

+                return;

+            else              

+                node = (ObjectProfileNode) queue.removeFirst ();

+        }

+    }

+    

+    /*

+     * A helper method for manipulating a class metadata cache.

+     */

+    private static ClassMetadata getClassMetadata (final Class cls,

+                                                   final Map /* Class->ClassMetadata */ metadataMap,

+                                                   final ClassAccessPrivilegedAction caAction,

+                                                   final FieldAccessPrivilegedAction faAction)

+    {

+        if (cls == null) return null; 

+        

+        ClassMetadata result;

+        synchronized (metadataMap)

+        {

+            result = (ClassMetadata) metadataMap.get (cls);

+        }

+        if (result != null) return result;

+

+        int primitiveFieldCount = 0;

+        int shellSize = OBJECT_SHELL_SIZE; // java.lang.Object shell

+        final List /* Field */ refFields = new LinkedList ();

+        

+        final Field [] declaredFields;

+        try

+        {

+            caAction.setContext (cls);

+            declaredFields = (Field []) AccessController.doPrivileged (caAction);

+        }

+        catch (PrivilegedActionException pae)

+        {

+            throw new RuntimeException ("could not access declared fields of class " + cls.getName () + ": " + pae.getException ());

+        }

+    

+        for (int f = 0; f < declaredFields.length; ++ f)

+        {

+            final Field field = declaredFields [f];

+            if ((Modifier.STATIC & field.getModifiers ()) != 0) continue;

+

+            final Class fieldType = field.getType ();

+            if (fieldType.isPrimitive ())

+            {

+                // memory alignment ignored:

+                shellSize += sizeofPrimitiveType (fieldType);

+                ++ primitiveFieldCount;

+            }

+            else

+            {

+                // prepare for graph traversal later:

+                if (! field.isAccessible ())

+                {

+                    try

+                    {

+                        faAction.setContext (field);

+                        AccessController.doPrivileged (faAction);

+                    }

+                    catch (PrivilegedActionException pae)

+                    {

+                        throw new RuntimeException ("could not make field " + field + " accessible: " + pae.getException ());

+                    }

+                }

+                

+                // memory alignment ignored:

+                shellSize += OBJREF_SIZE;

+                refFields.add (field);

+            }

+        }

+        

+        // recurse into superclass:

+        final ClassMetadata superMetadata = getClassMetadata (cls.getSuperclass (), metadataMap, caAction, faAction);

+        if (superMetadata != null)

+        {

+            primitiveFieldCount += superMetadata.m_primitiveFieldCount;

+            shellSize += superMetadata.m_shellSize - OBJECT_SHELL_SIZE;

+            refFields.addAll (Arrays.asList (superMetadata.m_refFields));

+        }

+        

+        final Field [] _refFields = new Field [refFields.size ()];

+        refFields.toArray (_refFields);

+        

+        result = new ClassMetadata (primitiveFieldCount, shellSize, _refFields);

+        synchronized (metadataMap)

+        {

+            metadataMap.put (cls, result);

+        }

+        

+        return result;        

+    } 

+    

+    

+    /*

+     * Computes the "shallow" size of an array instance.

+     */

+    private static int sizeofArrayShell (final int length, final Class componentType)

+    {

+        // this ignores memory alignment issues by design:

+        

+        final int slotSize = componentType.isPrimitive ()

+            ? sizeofPrimitiveType (componentType)      

+            : OBJREF_SIZE;

+            

+        return OBJECT_SHELL_SIZE + INT_FIELD_SIZE + OBJREF_SIZE + length * slotSize;

+    }

+    

+    /*

+     * Returns the JVM-specific size of a primitive type.

+     */

+    private static int sizeofPrimitiveType (final Class type)

+    {

+        if (type == int.class)

+            return INT_FIELD_SIZE;

+        else if (type == long.class)

+            return LONG_FIELD_SIZE;

+        else if (type == short.class)

+            return SHORT_FIELD_SIZE;

+        else if (type == byte.class)

+            return BYTE_FIELD_SIZE;

+        else if (type == boolean.class)

+            return BOOLEAN_FIELD_SIZE;

+        else if (type == char.class)

+            return CHAR_FIELD_SIZE;

+        else if (type == double.class)

+            return DOUBLE_FIELD_SIZE;

+        else if (type == float.class)

+            return FLOAT_FIELD_SIZE;

+        else

+            throw new IllegalArgumentException ("not primitive: " + type);

+    }

+    

+    

+    // class metadata cache:

+    private static final Map CLASS_METADATA_CACHE = new WeakHashMap (101);

+

+} // end of class

+// ----------------------------------------------------------------------------
\ No newline at end of file
diff --git a/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectShellProfileNode.java b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectShellProfileNode.java
new file mode 100644
index 0000000..1a257ee
--- /dev/null
+++ b/spring-tutorial/src/main/java/com/supwisdom/tutorial/sizeof/ObjectShellProfileNode.java
@@ -0,0 +1,38 @@
+

+package com.supwisdom.tutorial.sizeof;

+

+// ----------------------------------------------------------------------------

+/**

+ * A shell pseudo-node implementation for a non-array class.

+ * 

+ * @author (C) <a href="http://www.javaworld.com/columns/jw-qna-index.shtml">Vlad Roubtsov</a>, 2003

+ */

+final class ObjectShellProfileNode extends AbstractShellProfileNode

+{

+    // public: ................................................................    

+

+    public String name ()

+    {

+        return "<shell: " + m_primitiveFieldCount + " prim/" + m_refFieldCount + " ref fields>";

+    }

+    

+    // protected: .............................................................

+

+    // package: ...............................................................

+    

+    

+    ObjectShellProfileNode (final IObjectProfileNode parent, final int primitiveFieldCount, final int refFieldCount)

+    {

+        super (parent);

+        

+        m_primitiveFieldCount = primitiveFieldCount;

+        m_refFieldCount = refFieldCount;

+    }

+    

+    // private: ...............................................................

+    

+

+    private final int m_primitiveFieldCount, m_refFieldCount;

+

+} // end of class

+// ----------------------------------------------------------------------------
\ No newline at end of file