Zim-Zam O'Pootertoot Zim-Zam O'Pootertoot - 5 months ago 8
Java Question

Determine the amount of memory being allocated in a line of code

I'm relatively new to performance testing, and am trying to determine how much memory is being allocated per line of code. I tried applying the MemoryMXBean to the problem, but I'm getting nonsense results.

I'm taking a snapshot of the heap usage before the line of code, then I'm taking a snapshot after the line of code, and finally I'm storing the difference if the amount of heap usage increased (assuming that the garbage collector ran if it decreased); the

getSnapshot()
method is run from my test code and is used to reset the counters and print the results. However, this test is telling me that we're allocating over 20 mb per line, even on lines that don't do any allocation. If I take the median instead of the mean then I still get nonsense telling me that we're allocating 47 mb per line.

How do I determine how much memory is being allocated in a line of code, and/or what am I doing wrong with my use of the
MemoryMXBean
?

private static final MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();

private static final Map<Line, Tuple> lineMap;

static {
Map<Line, Tuple> map = new EnumMap<Line, Tuple>(Line.class);
for(Line line : Line.values()) {
map.put(line, new Tuple());
}
lineMap = map;
}

public enum Line {
line01,
line02,
line03,
...
}

public static Map<String, Long> getSnapshot() {
Map<String, Long> returnVal = new HashMap<>();
for(Map.Entry<Line, Tuple> entry : lineMap.entrySet()) {
if(entry.getValue().count.get() > 0) {
returnVal.put(entry.getKey().toString(), entry.getValue().memory.getAndSet(0) / entry.getValue().count.getAndSet(0));
} else {
entry.getValue().memory.set(0);
}
}
return returnVal;
}

private static class Tuple {
public final AtomicLong memory = new AtomicLong();
public final AtomicLong count = new AtomicLong();
}

private static void update(Line line, long value) {
long finish = memBean.getHeapMemoryUsage().getUsed();

// ignore if the gc ran
if(finish > value) {
Tuple tuple = lineMap.get(line);
tuple.memory.addAndGet(finish - value);
tuple.count.incrementAndGet();
}
}

public void codeIamTesting {
long start = memBean.getHeapMemoryUsage().getUsed();
// line1
update(Line.line01, start);
}

Answer

When I disable the thread-local allocation buffer (command line argument -XX:-UseTLAB) I get sane results - the memBean.getHeapMemoryUsage().getUsed() call adds some overhead to the results, but it's easy enough to determine what this overhead is (240 bytes in my case) and subtract it from the data. It appears that when using -XX:+UseTLAB (which is the default) the getUsed() snapshot only changes when a new buffer is allocated, resulting in most allocations appearing to use zero bytes of heap while a few allocations use several megabytes of heap. I also used -XX:+UseParallelGC to reduce the amount of jitter that I would have gotten with -XX:+UseG1GC

Comments