2

I was planning to use getRuntime().freeMemory() to get a rough estimate of how much memory is consumed by a java method . I was trying to achieve it with the following:

long beforeUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
foo();
long afterUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
long actualMemUsed=afterUsedMem-beforeUsedMem;

For testing if this could actually work, I ran the code below

long beforeUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
List<Integer> list = new ArrayList<>();
for(int i = 0 ;i<1000;i++)
  list.add(i);

long afterUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
long actualMemUsed=afterUsedMem-beforeUsedMem;
System.out.println("mem used "+actualMemUsed);

However, the output of the program comes out to be

mem used 0

However, if I loop from 0 -> 100000 , the output comes out to be

mem used 2684368

Can anyone explain why jvm may be working like this?

AlBlue
  • 23,254
  • 14
  • 71
  • 91
95_96
  • 341
  • 2
  • 12
  • Total and free are equal so result is 0. Then you use some memory so the value is not 0 anymore? – MissingSemiColon Jun 26 '20 at 11:39
  • @MissingSemiColon why would free memory be same after i have added to list? – 95_96 Jun 26 '20 at 12:35
  • Total memory = 10.000.000,Free memory = 10.000.000 After using memory: Total memory = 10.000.000,Free memory = 8.000.000 https://stackoverflow.com/questions/3571203/what-are-runtime-getruntime-totalmemory-and-freememory. Cant say i would expect it to be 0 neither, but its the only logical explanation I could think of sry. – MissingSemiColon Jun 26 '20 at 14:26
  • 2
    Garbage collection can take place at any point of time. Further, an arbitrary amount of reclaimable garbage may already exist at the beginning of your code’s execution. So the difference is arbitrary and meaningless. The number can even be negative, e.g. when preexisting garbage occupied more memory than objects allocated in your code. – Holger Jun 26 '20 at 14:39

1 Answers1

1

The problem is that getRuntime().freeMemory() doesn't do what you think it is doing.

The JVM will allocate memory in chunks as it needs them, and depending on the GC you've used, will potentially return memory to the OS as well. So the results that you see from the memory options are dependent on what the JVM is doing, and not what an individual method does.

If you want to measure the amount of objects allocated by a Java method, you should look at JMH and specifically the GC profiler. This will run your method repeatedly, and then take an average based on information gathered by the garbage collection runs through JMX.

I wrote a blog post on the performance of StringBuilder which used JMH and prof gc. The source is available at GitHub for reference.

The TL;DR is that you want something like:

@State(Scope.Benchmark)
public class StringBuilderBenchmark {
    
    @Benchmark
    public String testEmptyBuffer() {
        StringBuffer buffer = new StringBuffer();
        return buffer.toString();
    }
}

You then compile/build this with JMH using the mvn jmh arhectype and run it with prof gc:

java -jar target/benchmarks.jar \
   -wi 5 -tu ns -f 1 -bm avgt -prof gc

It will then run the method repeatedly and give you an average of the number of bytes allocated per invocation of your method.

Note that it will not measure one-off initialisations required; if, for example, you lazily allocate a giant Map with a million entries, it won't be counted by this process.

More information on JMH is available on the JMH homepage.

AlBlue
  • 23,254
  • 14
  • 71
  • 91