This is continuation of What's in my Java heap? post. Let us see more OQL (Object Query Language) examples. But before that we will digress into built-in functions supported in OQL.
The built-in functions in OQL fall into the following categories:There is also built-in object called heap. There are various useful methods in heap object. For more details on built-in functions, object refer to "OQL Help" link in jhat's OQL page.
Now, let us see some interesting queries.Select all objects referred by a SoftReference:
select f.referent from java.lang.ref.SoftReference f
where f.referent != null
referent is a private field of java.lang.ref.SoftReference class (actually inherited field from java.lang.ref.Reference. You may use javap -p to find these!) We filter the SoftReferences that have been cleared (i.e., referent is null).
Show referents that are not referred by another object. i.e., the referent is reachable only by that soft reference:
select f.referent from java.lang.ref.SoftReference f
where f.referent != null && referrers(f.referent).length == 1
Note that use of referrers built-in function to find the referrers of a given object. because referrers returns an array, the result supports length property.
Let us refine above query. We want to find all objects that referred only by soft references but we don't care how many soft references refer to it. i.e., we allow more than one soft reference to refer to it.
select f.referent from java.lang.ref.SoftReference f
where f.referent != null &&
filter(referrers(f.referent), "classof(it).name != 'java.lang.ref.SoftReference'").length == 0
Note that filter function filters the referrers array using a boolean expression. In the filter condition we check the class name of referrer is not java.lang.ref.SoftReference. Now, if the filtered arrays contains atleast one element, then we know that f.referent is referred from some object that is not of type java.lang.ref.SoftReference!
Find all finalizable objects (i.e., objects that are some class that has 'java.lang.Object.finalize()' method overriden)
select f.referent from java.lang.ref.Finalizer f
where f.referent != null
How does this work? When an instance of a class that overrides finalize() method is created (potentially finalizable object), JVM registers the object by creating an instance of java.lang.ref.Finalizer. The referent field of that Finalizer object refers to the newly created "to be finalized" object. (dependency on implementation detail!)
Find all finalizable objects and approximate size of the heap retained because of those.
select { obj: f.referent, size: sum(map(reachables(f.referent), "sizeof(it)")) }
from java.lang.ref.Finalizer f
where f.referent != null
Ah! That looks really complex -- but, actually it is simple. I use JavaScript object literal to select multiple values in the select expression (obj and size properties). reachables finds objects reachable from given object. map creates a new array from input array by applying given expression on each element. The map call in this query would create an array of sizes of each reachable object. sum built-in adds all elements of array. So, we get total size of reachable objects from given object (f.referent in this case). Why do I say approximate size? HPROF binary heap dump format does not account for actual bytes used in live JVM. Instead sizes just enough to hold the data are used. For eg. JVMs would align smaller data types such as 'char' -- JVMs would use 4 bytes instead of 2 bytes. Also, JVMs tend to use one or two header words with each object. All these are not accounted in HPROF file dump. HPROF uses minimal size needed to hold the data - for example 2 bytes for a char, 1 byte for a boolean and so on.
That's all for now! We will more interesting OQL queries in future. Stay tuned...
For example, based on the Finalizer example, something like:
select sum(*) from { select sum(map(reachables(f.referent), "sizeof(it)")) from java.lang.ref.Finalizer f where f.referent != null }???Posted by Gordon Mohr on November 22, 2005 at 10:54 PM IST #
Posted by A. Sundararajan on November 23, 2005 at 10:23 AM IST #
But, the behavior I'm seeing is still mysterious.
For example, I'm unclear why this...
select sum(heap.objects("sun.util.calendar.ZoneInfo"),"sizeof(it)")...returns a concatenated string of all instance sizes, rather than a true total.Because of that, the closest I can get to the result I want is to keep a running average, and ignore all but the last value reported, like this:
select map(heap.objects("java.lang.String"), function tally(obj) { if(typeof tally.total == 'undefined') { tally.total = 0; tally.count = 0; } tally.total += sum(map(reachables(obj), "sizeof(it)")); tally.count++; return tally.total/tally.count; } );But, it seems like there should be a simpler way...Thanks for your help!
Posted by Gordon Mohr on November 23, 2005 at 05:44 PM IST #
Posted by A. Sundararajan on November 24, 2005 at 04:59 PM IST #
Posted by Peter Arrenbrecht on June 27, 2006 at 10:48 AM IST #
Posted by qw on September 17, 2006 at 09:35 PM IST #
Posted by David Hunnisett on March 30, 2007 at 09:13 PM IST #
Posted by A. Sundararajan on March 30, 2007 at 10:03 PM IST #
Posted by C. Simpson on April 19, 2007 at 04:43 AM IST #
Posted by A. Sundararajan on April 20, 2007 at 10:32 AM IST #