|
|
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 4 of 6
Of course, profile()'s output is useful only if I have a good way to explore it. To this end, every IObjectProfileNode supports examination by a node visitor together with a node filter:
interface IObjectProfileNode
{
interface INodeFilter
{
boolean accept (IObjectProfileNode node);
} // End of nested interface
interface INodeVisitor
{
/**
* Pre-order visit.
*/
void previsit (IObjectProfileNode node);
/**
* Post-order visit.
*/
void postvisit (IObjectProfileNode node);
} // End of nested interface
boolean traverse (INodeFilter filter, INodeVisitor visitor);
...
} // End of interface
A node visitor gets a shot at doing something with a tree node only if the accompanying filter is null or if the filter accepts the node. For simplicity, the node's children are examined only if the node itself has been examined.
Both pre- and post-order visits are supported. The size contributions from the java.lang.Object shell plus all primitive data fields are lumped together in a pseudo-node attached to every "real" node representing an object
instance. Such shell nodes are accessible via IObjectProfileNode.shell() and also show up in the IObjectProfileNode.children() list: the idea is to be able to write data filters and visitors that consider primitive data overhead on equal footing with
instantiable data types.
It is up to you how to implement filters and visitors. As a starting point, the ObjectProfileFilters class (see this article's download) offers several useful stock filters that help prune large object trees based on node size, node size relative to its parent's
size, node size relative to the root object, and so on. The ObjectProfilerVisitors class contains the default visitor used by IObjectProfileNode.dump(), as well as a visitor that can create an XML dump for more sophisticated object browsing. It is also easy to turn a profile
into a Swing TreeModel.
As an illustration, let's do a full dump of the two-string array object mentioned above:
public class Main
{
public static void main (String [] args)
{
Object obj = new String [] {new String ("JavaWorld"),
new String ("JavaWorld")};
IObjectProfileNode profile = ObjectProfiler.profile (obj);
System.out.println ("obj size = " + profile.size () + " bytes");
System.out.println (profile.dump ());
}
} // End of class
This code produces:
obj size = 106 bytes
106 -> <INPUT> : String[]
58 (54.7%) -> <INPUT>[0] : String
34 (32.1%) -> String#value : char[], refcount=2
34 (32.1%) -> <shell: char[], length=9>
24 (22.6%) -> <shell: 3 prim/1 ref fields>
24 (22.6%) -> <shell: String[], length=2>
24 (22.6%) -> <INPUT>[1] : String
24 (22.6%) -> <shell: 3 prim/1 ref fields>
Indeed, as explained earlier, the internal character array (referenced by java.lang.String#value) is shared between both strings. Even though ObjectProfiler.profile() assigns the ownership of this array to the first discovered string, it notices that the array is shared (shown by refcount=2 next to it).
ObjectProfiler.profile() creates a node graph whose size overhead is typically several times the size of the original object graph. If you are interested
only in the root object size, you can use a faster and more efficient ObjectProfiler.sizeof() method (see this article's download for the actual code), implemented via a nonrecursive depth-first traversal.
Archived Discussions (Read only)