Assessing Java object size in Matlab

Have you noticed that all Java object references are displayed as using 0 bytes in the Matlab Workspace browser and the whos function? This is not a bug, but in fact a deliberate design decision, in order to avoid the need to calculate the deep-memory usage of Java references (i.e., objects that include references to other objects etc.).

Well, sometimes it so happens that we really need to know the size of the Java object, or the size difference between two objects (to help resolve memory leaks, for example). There are several resources online that explain how to do this in Matlab (examples 1, 2, 3). Today I will show two alternatives that I found useful within the context of Matlab:

ObjectProfiler

A full decade ago, Vladimir Roubtsov posted a very detailed article in the JavaWorld magazine explaining how to profile and report Java object sizes. The article contained a downloadable Java archive that we can easily use in Matlab. After downloading the zip file, extract the contained objectprofiler.jar file, add it to the Java classpath and start using it, as follows:

ans =
28

Note that the reported sizes may be different on different Matlab releases (=JVM versions) and platforms. Also note that the reported size (28 bytes for the Java Color object) is much smaller than the size required to serialize the object into a byte stream (408 bytes in this case), as I’ve shown in last week’s article.

Running the sizeof method on deeply referenced objects could quickly exhaust Matlab’s memory:

>> jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;

% on R2012a: takes a long time and finally reports
ans =
72011200

% on R2014a: takes a long time and finally croaks
% (which is not surprising, considering the Desktop's new toolstrip)
Java exception occurred:
java.lang.OutOfMemoryError: Java heap space
at java.util.IdentityHashMap.resize(Unknown Source)
at java.util.IdentityHashMap.put(Unknown Source)

ObjectProfiler has a very handy feature of enabling a visual display of the object’s reference tree. For example:

>> jObject = java.util.Hashtable
jObject =
{}
ans =
105
ans =
105 -> <INPUT> : Hashtable
60 (57.1%) -> Hashtable#table : Hashtable$Entry[] 60 (57.1%) -> <shell: Hashtable$Entry[], length=11>
45 (42.9%) -> <shell: 6 prim/4 ref fields>

>> jObject.put('key1',1.23);
ans =
189
ans =
189 -> <INPUT> : Hashtable
144 (76.2%) -> Hashtable#table : Hashtable$Entry[] 84 (44.4%) -> Hashtable#table[4] : Hashtable$Entry
44 (23.3%) -> Hashtable$Entry#key : String 24 (12.7%) -> String#value : char[] 24 (12.7%) -> <shell: char[], length=4> 20 (10.6%) -> <shell: 2 prim/1 ref fields> 24 (12.7%) -> <shell: 1 prim/3 ref fields> 16 (8.5%) -> Hashtable$Entry#value : Double
16 (8.5%) -> <shell: 1 prim/0 ref fields>
60 (31.7%) -> <shell: Hashtable\$Entry[], length=11>
45 (23.8%) -> <shell: 6 prim/4 ref fields>

As we can see, adding the 'key1' key to the hashtable object actually added 2 new references: a 44-byte String and a 16-byte Double, plus 24 additional overhead bytes, for a total addition of 84 bytes.

ObjectProfiler has a convenience method sizedelta(jObject1,jObject2) which returns the size delta in bytes between the two specified objects. There are a few additional methods for ObjectProfiler and the ObjectProfiler.profile() object – interested readers are referred to the original article and to the source code (which is included within the zip file that we downloaded).

Classmexer

The Classmexer utility works a bit differently but is also very easy to use once the initial setup is done. First we need to download the zip file, then extract the classmexer.jar and place it in your Matlab’s startup folder. In that same folder, edit (create if necessary) a java.opts file with the following line:

-javaagent:classmexer.jar

After restarting Matlab, we can use Classmexer as follows:

>> com.javamex.classmexer.MemoryUtil.deepMemoryUsageOf(java.awt.Color.red)
ans =
32

>> jObject = java.util.Hashtable;
>> com.javamex.classmexer.MemoryUtil.deepMemoryUsageOf(jObject)
ans =
120
>> jObject.put('key1',1.23); jObject
jObject =
{key1=1.23}
>> com.javamex.classmexer.MemoryUtil.deepMemoryUsageOf(jObject)
ans =
264

Note how the values reported by Classmexer differ from those of ObjectProfiler. To tell the truth, I’m not sure which of them to believe: ObjectProfiler seems more detailed, but Classmexer uses Java’s preferred mechanism of using an instrumentation agent.

We can also use java.lang.Runtime.getRuntime‘s methods (maxMemory(), freeMemory() and totalMemory()) to monitor overall Java memory (note a MathWorks blog article on this). Note that this reports the total memory values, and fluctuates (sometimes dramatically) from second to second, as Matlab’s desktop and other Java-heavy tools create Java objects, which the JVM garbage-collects.

Jeff Gullet has suggested to monitor these values and programmatically activate a synchronous Java garbage-collection when the memory appears too “crowded” (I fixed Jeff’s posted idea in the snippet below):

r = java.lang.Runtime.getRuntime;
if (r.freeMemory/r.totalMemory) < 0.1
r.gc();
end

A MathWorks technical article provided some assistance on using the JConsole utility to profile Java memory in Matlab. We can also use the JMap and JHat utilities. All these utilities are part of the free Java Development Kit (JDK) that can be downloaded online, just ensure you’re using the same Java version as reported by Matlab:

>> version -java
ans =
Java 1.7.0_11-b21   % i.e., Java 7 update 11

In addition to the JDK tools, I find the open-source JVisualVM utility informative and easy to use. We can also use JMP (R2007a and earlier), TIJMP (R2007b and later), or other 3rd-party tools. A list of Java-centric resources is available in the Java SE Troubleshooting guide.

To complete the picture, a couple of years ago I posted an article on profiling Matlab’s memory usage, which included a section on Java memory. You may also find useful another article I wrote, on finding and fixing a Java memory leak in Matlab.

Categories: Java, Medium risk of breaking in future versions

Tags: ,

 Print