Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get the memory that my Java program uses via Java's Runtime api?

There are similar questions out there, but they seem to avoid answering this specific question. How can I get the memory that my Java program uses via Java's Runtime api?

The answer here indicates that I can do something like this:

System.out.println("KB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024); 

But this always returns the same number, no matter which program I run. For example, below I have a program where no matter how many numbers I put in the map, the memory usage stays the same.

package memoryTest;  import java.util.HashMap; import java.util.Map;  public class MemoryTest {      static Map<Integer, NewObject> map = new HashMap<Integer, NewObject>();      public static void main(String[] args){          System.out.println("KB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024);         fillMemory(25);          System.out.println("KB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024);     }      static int j=0;     public static void fillMemory(int i){          for(int k=0; k< 2000; k++)             map.put(j++, new NewObject());      }           public static class NewObject{         long i = 0L;         long j = 0L;         long k = 0L;     }      } 

via cambecc's main method, output is:

3085, Total: 128516096, Free: 127173744, Diff: 671120

173579, Total: 128516096, Free: 110033976, Diff: 671128

335207, Total: 128516096, Free: 92417792, Diff: 637544

672788, Total: 224198656, Free: 159302960, Diff: 1221520

1171480, Total: 224198656, Free: 106939136, Diff: 1221544

1489771, Total: 368377856, Free: 227374816, Diff: 1212984

1998743, Total: 368377856, Free: 182494408, Diff: 1212984

like image 822
Matt Avatar asked Jun 28 '13 22:06

Matt


People also ask

How do I see how much memory is used in Java?

totalMemory() method returns the total amount of memory in the Java virtual machine. The value returned by this method may vary over time, depending on the host environment. Note that the amount of memory required to hold an object of any given type may be implementation-dependent.

What is runtime memory in Java?

The Runtime#maxMemory method returns the maximum memory that the JVM will attempt to use. Once the JVM memory usage reaches this value, then it will not allocate more memory and instead, and it will garbage collect more frequently.


2 Answers

You're doing it correctly. The way to get memory usage is exactly as you described:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory() 

But the reason your program always returns the same memory usage is because you are not creating enough objects to overcome the precision limitations of the freeMemory method. Although it has byte resolution, there is no guarantee for how precise freeMemory needs to be. The javadoc says as much:

an approximation to the total amount of memory currently available for future allocated objects, measured in bytes.

Try the following, which creates two million NewObject instances, and prints out each time the result of freeMemory changes:

public static void main(String[] args) {     Runtime rt = Runtime.getRuntime();     long prevTotal = 0;     long prevFree = rt.freeMemory();      for (int i = 0; i < 2_000_000; i++) {         long total = rt.totalMemory();         long free = rt.freeMemory();         if (total != prevTotal || free != prevFree) {             System.out.println(                 String.format("#%s, Total: %s, Free: %s, Diff: %s",                     i,                      total,                     free,                     prevFree - free));             prevTotal = total;             prevFree = free;         }         map.put(i, new NewObject());     } } 

On my machine, I see output like the following

#0, Total: 513998848, Free: 508635256, Diff: 0 #21437, Total: 513998848, Free: 505953496, Diff: 2681760 #48905, Total: 513998848, Free: 503271728, Diff: 2681768 #73394, Total: 513998848, Free: 500589960, Diff: 2681768 #103841, Total: 513998848, Free: 497908192, Diff: 2681768 ... 

Notice how the reported free memory did not change until the 21,437th object was instantiated? The numbers suggest freeMemory for the JVM I'm using (Java7 Win 64-bit) has a precision of just over 2.5MB (although if you run the experiment, you'll see this number varies).

-- Edit --

This code is the same as above, but prints more details about memory usage. Hopefully it's a bit clearer how the JVM's memory usage behaves. We continuously allocate new objects in a loop. During each iteration, if the totalMemory or freeMemory is the same as the last iteration, we don't print anything. But if either has changed, we report current memory usage. The values represent the difference between current usage and the previous memory report.

public static void main(String[] args) {     Runtime rt = Runtime.getRuntime();     long prevTotal = 0;     long prevFree = rt.freeMemory();      for (int i = 0; i < 2_000_000; i++) {         long total = rt.totalMemory();         long free = rt.freeMemory();         if (total != prevTotal || free != prevFree) {             long used = total - free;             long prevUsed = (prevTotal - prevFree);             System.out.println(                 "#" + i +                 ", Total: " + total +                 ", Used: " + used +                 ", ∆Used: " + (used - prevUsed) +                 ", Free: " + free +                 ", ∆Free: " + (free - prevFree));             prevTotal = total;             prevFree = free;         }         map.put(i, new NewObject());     } } 

On my notebook, I see the following output. Note your results will differ depending on OS, hardware, JVM implementation, etc.:

#0, Total: 83427328, Used: 1741048, ∆Used: 83427328, Free: 81686280, ∆Free: 0 #3228, Total: 83427328, Used: 1741080, ∆Used: 32, Free: 81686248, ∆Free: -32 #3229, Total: 83427328, Used: 2176280, ∆Used: 435200, Free: 81251048, ∆Free: -435200 #7777, Total: 83427328, Used: 2176312, ∆Used: 32, Free: 81251016, ∆Free: -32 #7778, Total: 83427328, Used: 2611536, ∆Used: 435224, Free: 80815792, ∆Free: -435224 ... #415056, Total: 83427328, Used: 41517072, ∆Used: 407920, Free: 41910256, ∆Free: -407920 #419680, Total: 145358848, Used: 39477560, ∆Used: -2039512, Free: 105881288, ∆Free: 63971032 #419681, Total: 145358848, Used: 40283832, ∆Used: 806272, Free: 105075016, ∆Free: -806272 ... 

There are a few observations from this data:

  1. Used memory tends to increase, as expected. Used memory includes live objects and garbage.
  2. But used memory decreases during a GC, because garbage has been discarded. For example, this occurred at #419680.
  3. The amount of free memory reduces in chunks, not byte-by-byte. The chunks vary in size. Sometimes the chunks are really tiny, like 32 bytes, but usually they are larger, like 400K, or 800K. So it appears the chunk size will vary a fair bit. But compared to total heap size, the variation appears tiny. For example, at #419681 the chunk size is only 0.6% of the total heap size.
  4. Free memory tends to decrease, as expected, until a GC kicks in and cleans up garbage. When this occurs, free memory increases pretty dramatically, depending on the amount of discarded garbage.
  5. This test generates a lot of garbage. As the hashmap grows in size, it rehashes its contents, thus generating a lot of garbage.
like image 190
cambecc Avatar answered Oct 03 '22 03:10

cambecc


I've got the following methods

public static long getMaxMemory() {     return Runtime.getRuntime().maxMemory(); }  public static long getUsedMemory() {     return getMaxMemory() - getFreeMemory(); }  public static long getTotalMemory() {     return Runtime.getRuntime().totalMemory(); }  public static long getFreeMemory() {     return Runtime.getRuntime().freeMemory(); } 

which return the (used) Memory in bytes.

If you want to recalculate to MiB I've got:

private static final long MEGABYTE_FACTOR = 1024L * 1024L; private static final DecimalFormat ROUNDED_DOUBLE_DECIMALFORMAT; private static final String MIB = "MiB";  static {     DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(Locale.ENGLISH);     otherSymbols.setDecimalSeparator('.');     otherSymbols.setGroupingSeparator(',');     ROUNDED_DOUBLE_DECIMALFORMAT = new DecimalFormat("####0.00", otherSymbols);     ROUNDED_DOUBLE_DECIMALFORMAT.setGroupingUsed(false); }       public static String getTotalMemoryInMiB() {         double totalMiB = bytesToMiB(getTotalMemory());         return String.format("%s %s", ROUNDED_DOUBLE_DECIMALFORMAT.format(totalMiB), MIB);     }      public static String getFreeMemoryInMiB() {         double freeMiB = bytesToMiB(getFreeMemory());         return String.format("%s %s", ROUNDED_DOUBLE_DECIMALFORMAT.format(freeMiB), MIB);     }      public static String getUsedMemoryInMiB() {         double usedMiB = bytesToMiB(getUsedMemory());         return String.format("%s %s", ROUNDED_DOUBLE_DECIMALFORMAT.format(usedMiB), MIB);     }      public static String getMaxMemoryInMiB() {         double maxMiB = bytesToMiB(getMaxMemory());         return String.format("%s %s", ROUNDED_DOUBLE_DECIMALFORMAT.format(maxMiB), MIB);     }      public static double getPercentageUsed() {         return ((double) getUsedMemory() / getMaxMemory()) * 100;     }      public static String getPercentageUsedFormatted() {         double usedPercentage = getPercentageUsed();         return ROUNDED_DOUBLE_DECIMALFORMAT.format(usedPercentage) + "%";     } 
like image 35
Pwnstar Avatar answered Oct 03 '22 05:10

Pwnstar