How to tell whether a section of java code is heap-allocation-free?

Viewed 100

I have a java function whose documentation claims that it does no heap memory allocations.

How can I verify whether it (still) behaves as claimed, at runtime?

Ideally, I'd like to do this during a normal (production) program run, in a way that can be introspected by the program and shown as program output.

I'm hoping for a simple solid runtime API such as:

  • set checkpoint A, call the function, set checkpoint B, ask whether any heap memory allocations occurred in this thread between A and B.
  • or, temporarily turn on "throw an exception if a heap memory allocation occurs in this thread" mode.

If that's not possible, I'd like to at least be able to do the equivalent during some kind of debug-mode / profiling run.

Mainly I'm interested in doing this at runtime, but if it's possible to do it using static analysis tools, I'd be interested in that too.

I've searched the web and found nothing. There are memory profilers, e.g. JFR that can do things like report and break down current heap usage at any given time, but I haven't seen any evidence that they can answer this simple precise yes-or-no "did any allocations happen in this thread during this interval" query.

3 Answers

You can totally do this with runtime instrumentation. You may not be able to do it for an isolated part of your program, but you can set up something like https://github.com/google/allocation-instrumenter for an isolated run of your program (or a sampling of runs in production). You might use it, for example, like so:

public class Test {
  public static void main(String [] args) throws Exception {
    AtomicInteger allocations = new AtomicInteger();

    AllocationRecorder.addSampler(new Sampler() {
      public void sampleAllocation(int count, String desc, Object newObj, long size) {
         allocations.getAndIncrement();
      }
    });
    codeBlock(); // do any initialization, classloading first!
    int expectedAllocations = allocations.get();
    codeBlock();
    assert allocations.get() == expectedAllocations; // no more allocations
  }
}

You can profile memory usage in a controlled environment by using Runtime#freeMemory(), disabling GC (-XX:+UseEpsilonGC), and running only the logic to test.

// Logic to test (to load classes, do one-off initializations)
Runtime runtime = Runtime.getRuntime();
long start = runtime.freeMemory();
// Logic to test
long end = runtime.freeMemory();
assert start == end;

I don't think there's a way to test this other than checking for instances of new in the method and any methods that it calls.

Memory Allocation Tests: It is impossible to isolate the effects of the method.

Benchmarking: You can test just running the method in isolation, but that will fail in the case where allocations in production would be made conditionally based on other resources being present, like

if (MyOtherClass.myOtherVariable != null) {
  int[] arr = new int[100];
}

In this case memory allocation is conditional on some other step that may or may not have occurred.

Related