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...