在做了所有这些预备之后,下面就是这种图形遍历的标准实现: 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 (); } } 该代码是上一篇Java Q&A, "Attack of the Clones."使用的"通过反射克隆"实现的远亲。
|