Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 6
Thinking about graph traversals and shortest paths should ring a bell at this point: breadth-first search is a graph traversal algorithm that guarantees to find the shortest path from the starting node to any other reachable graph node.
After all these preliminaries, here is a textbook implementation of such a graph traversal. (Some details and auxiliary methods have been omitted; see this article's download for full details.):
public static IObjectProfileNode profile (Object obj)
{
final IdentityHashMap visited = new IdentityHashMap ();
final ObjectProfileNode root = createProfileTree (obj, visited,
CLASS_METADATA_CACHE);
finishProfileTree (root);
return root;
}
private static ObjectProfileNode createProfileTree (Object obj,
IdentityHashMap visited,
Map 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;
}
private static void finishProfileTree (ObjectProfileNode node)
{
final LinkedList queue = new LinkedList ();
IObjectProfileNode lastFinished = null;
while (node != null)
{
// Note that an unfinished nonshell 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 ();
}
}
This code is a distant relative of the clone-via-reflection implementation used in a past Java Q&A, "Attack of the Clones." As before, it caches reflection metadata to improve performance and uses an identity hashmap to mark visited objects. The
profile() method starts by spanning the original object graph with a tree of IObjectProfileNodes in a breadth-first traversal and finishes with a quick post-order traversal that totals and assigns all node sizes. profile() returns a IObjectProfileNode that is the generated spanning tree's root, and its size() is the entire graph's size.
Archived Discussions (Read only)