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:
>> javaaddpath 'C:\path\to\where\you\placed\your\copy\of\objectprofiler.jar' >> com.vladium.utils.ObjectProfiler.sizeof(java.awt.Color.red) 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 >> com.vladium.utils.ObjectProfiler.sizeof(jDesktop) ans = 72011200 % on R2014a: takes a long time and finally croaks % (which is not surprising, considering the Desktop's new toolstrip) >> com.vladium.utils.ObjectProfiler.sizeof(jDesktop) Java exception occurred: java.lang.OutOfMemoryError: Java heap space at java.util.IdentityHashMap.resize(Unknown Source) at java.util.IdentityHashMap.put(Unknown Source) at com.vladium.utils.ObjectProfiler.computeSizeof(ObjectProfiler.java:329) at com.vladium.utils.ObjectProfiler.sizeof(ObjectProfiler.java:85)
ObjectProfiler
has a very handy feature of enabling a visual display of the object’s reference tree. For example:
>> jObject = java.util.Hashtable jObject = {} >> com.vladium.utils.ObjectProfiler.sizeof(jObject) ans = 105 >> com.vladium.utils.ObjectProfiler.profile(jObject).dump 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); >> com.vladium.utils.ObjectProfiler.sizeof(jObject) ans = 189 >> com.vladium.utils.ObjectProfiler.profile(jObject).dump 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.
Related resources
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.