One thing that crops up a lot at my employer is the need to take an action on every allocation. This can happen in a lot of different contexts:

The programmer has a task, and wants to know how much memory the task allocates, so wants to increment a counter on every allocation.

The programmer wants to keep a histogram of most frequently accessed call sites.

The programmer wants to prevent a task from allocating too much memory, so it keeps a counter on every allocation and throws an exception when the counter reaches a certain value.

Because of the demand for this, a few of us put together a tool that instruments your code and invokes a callback on every allocation. The Allocation Instrumenter is a Java agent written using the java.lang.instrument API and ASM. Each allocation in your Java program is instrumented; a user-defined callback is invoked on each allocation.

The easiest way to explain this is with an example. Assume you have a program that creates 10 strings, and you want to instrument it:

I just allocated the object foo of type java/lang/String whose size is 24I just allocated the object foo of type java/lang/String whose size is 24I just allocated the object foo of type java/lang/String whose size is 24I just allocated the object foo of type java/lang/String whose size is 24I just allocated the object foo of type java/lang/String whose size is 24I just allocated the object foo of type java/lang/String whose size is 24I just allocated the object foo of type java/lang/String whose size is 24I just allocated the object foo of type java/lang/String whose size is 24I just allocated the object foo of type java/lang/String whose size is 24I just allocated the object foo of type java/lang/String whose size is 24

So, by my standards, it is really pretty easy to use. If you find it useful, please let me know!

Edited to add I noticed this on Twitter: Cool, even if it uses Ant (so probably I will never try it). This is funny, because I only added an ant buildfile so more people would try it. You can download the source and compile it with javac in about one line.

@Carsten - Eclipse MAT is designed to analyze a heap dump. This is designed to track individual allocation sites.

The three examples at the top of the post are the three main use cases I have found for this code. It can be very useful, for example, to have a histogram of call sites where the bulk of your allocation takes place. (We tend to sample the allocation call sites rather than track every one, because getting a stack trace at every allocation is very heavyweight).

@Patrick - It is pretty straightforward, which is why I OSS'd it. Lots of the other stuff we do internally involves writing loads of JVMTI and directly patching the JVM. I'd like to OSS that, too, but that would be a more ambitious effort.

DTrace actually has hooks that allow you to instrument allocations in a similar way, but the hooks have to be written in a DTrace-friendly way (i.e., not in Java). BTrace could hook into the DTrace hooks without too many problems. I recall a VM patch that was floating around a few months ago to enable it, but I don't know what became of it.

I'm wondering about the accuracy of the AllocationRecorder.getObjectSize(Object obj, boolean isArray) method. From what I see the cached value is always the size of the object that was first cached, not the size of the parameter object.

For example if I test with your test class with different sizes of strings the reported object size is always 40 for all the strings.

Was the performance of getting the object size from the instrumentation instance so poor that it necessitated this cache?

@Bruce - Yes, it was for performance. getObjectSize() was a dog. But all of your Strings probably are size 40 (that's kind of big, of course - 64-bit?). It's the backing char arrays that should be of variable size.

@Jaroslav - You're completely right! I had no idea. Since it looks as if you are a contributor - does it plug into the DTrace VM hooks, or does it do a similar bytecode rewrite?

1) If you want to defer the cost of constructing a stack trace, constructing a new Throwable means that you can defer the expensive creation of the stack trace until you need it. (Try creating a bunch of Throwables and comparing that cost to the creation of a bunch of stack traces)

2) You might secretly not need all of the stack traces. One trick is to gather a statistically valid sample. We've found that a decent sample is to grab one every time a thread allocates ~512K of objects, where ~ means a statistically valid sampling distributed around the number 512K.

3) It depends on how much allocation you do, of course. Simply instrumenting the code can cost anywhere from 5-10% to 50%, depending on the application.

I was very excited to find this. I've been scouring the web looking for such a tool. I tried your sample app and everything builds properly in Eclipse but I'm not getting any callbacks on the allocations. What should I check to determine the problem?

I got your sample app working. I have a couple more questions. I have a multi-threaded app and I'm wondering what the thread implications are. Is it thread safe? Do I set up a Sampler per thread and do I get callbacks that are thread specific? Or am I limited to a global Sampler which handles the callbacks of allocations for all threads?

Global sampler. Of course, you can have your sampler delegate to a Sampler stored in a ThreadLocal if you like. The performance would probably be even more questionable than the sampler typically is, though.

I was looking for something like this for a while, I think it is very useful! Is there any way to exclude classes from the instrumentation process? (e.g. The allocations from the asm classes). I'm trying to get used to the code but I've never worked with ASM. Thank you.

and it seems to me that the object allocated in the constructor is not being detected. I expect to see something like "I just allocated the object constructor of type java/lang/String whose size is ..." in the output, but this is not the case.

Oh I think I need to add more details. I added the sampler in the premain method in the AllocationInstrumenter. The code does detect a lot of object allocations, but not for the object created in the constructor.

About Me

I'm a programming languages and software engineering guy who works at Google. Nothing I say represents the views of my employer, of course.
I was one of the authors on JSR-133, the revision of the JLS that dealt with threads and synchronization. I often use this blog to address frequently asked questions about threading.