Java Get Available Memory

Java get available memory

This sample by William Brendel may be of some use.

EDIT: I originally provided this sample (linking to William Brendel's answer on another topic). The creator of that topic (Steve M) wanted to create a multi-platform Java application. Specifically, the user was trying to find a means by which to assess the running machine's resources (disk space, CPU and memory usage).

This is an inline transcript of the answer given in that topic. However, it has been pointed out on this topic that it is not the ideal solution, despite my answer being marked as accepted.

public class Main {
public static void main(String[] args) {
/* Total number of processors or cores available to the JVM */
System.out.println("Available processors (cores): " +
Runtime.getRuntime().availableProcessors());

/* Total amount of free memory available to the JVM */
System.out.println("Free memory (bytes): " +
Runtime.getRuntime().freeMemory());

/* This will return Long.MAX_VALUE if there is no preset limit */
long maxMemory = Runtime.getRuntime().maxMemory();
/* Maximum amount of memory the JVM will attempt to use */
System.out.println("Maximum memory (bytes): " +
(maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));

/* Total memory currently in use by the JVM */
System.out.println("Total memory (bytes): " +
Runtime.getRuntime().totalMemory());

/* Get a list of all filesystem roots on this system */
File[] roots = File.listRoots();

/* For each filesystem root, print some info */
for (File root : roots) {
System.out.println("File system root: " + root.getAbsolutePath());
System.out.println("Total space (bytes): " + root.getTotalSpace());
System.out.println("Free space (bytes): " + root.getFreeSpace());
System.out.println("Usable space (bytes): " + root.getUsableSpace());
}
}
}

User Christian Fries points out that it is wrong to assume that Runtime.getRuntime().freeMemory() gives you the amount of memory which may be allocated until an out-of-memory error occurs.

From the documentation, the signature return of Runtime.getRuntime().freeMemory() is as such:

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

However, user Christian Fries claims this function may be misinterpreted. He claims that the approximate amount of memory which may be allocated until an out-of-memory error occurs (the free memory) is likely to be given by:

long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;

With allocatedMemory being given by:

long allocatedMemory = 
(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());

The key here is a discrepancy between the concept of free memory. One thing is the memory that the operating system provides the Java Virtual Machine. Another is the total amount of bytes comprising the chunks of blocks of memory actually being used by the Java Virtual Machine itself.

Considering that memory given to Java applications is managed in blocks by the Java Virtual Machine, the amount of free memory available to the Java Virtual Machine may not exactly match the memory available for a Java application.

Specifically, Christian Fries denotes the usage of the -mx or -Xmx flags to set the maximum amount of memory available to the Java Virtual Machine. He notes the following function differences:

/* Returns the maximum amount of memory available to 
the Java Virtual Machine set by the '-mx' or '-Xmx' flags. */
Runtime.getRuntime().maxMemory();

/* Returns the total memory allocated from the system
(which can at most reach the maximum memory value
returned by the previous function). */
Runtime.getRuntime().totalMemory();

/* Returns the free memory *within* the total memory
returned by the previous function. */
Runtime.getRuntime().freeMemory();

Christian concludes his answer by stating that Runtime.getRuntime().freeMemory() in fact returns what may be called presumable free memory; even if a future memory allocation does not exceed the value returned by that function, if the Java Virtual Machine has not yet received the actual chunk of memory assigned by the host system, a java.lang.OutOfMemoryError may still be produced.

In the end, the proper method to use will have a varying degree of dependence on the specifics of your application.

I provide another link which may be useful. It is a question made by user Richard Dormand and answered by stones333 about determining the default Java heap size used.

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

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.

Is there any way to get total RAM memory in use by Java through all JVMs

Unfortunately, there is no such feature in the standard API. But you can use the extended API in a fail-safe way without explicitly referring to non-standard packages:

public class PhysicalMemory {
public static void main(String[] args) {
String[] attr={ "TotalPhysicalMemorySize", "FreePhysicalMemorySize"};
OperatingSystemMXBean op = ManagementFactory.getOperatingSystemMXBean();
List<Attribute> al;
try {
al = ManagementFactory.getPlatformMBeanServer()
.getAttributes(op.getObjectName(), attr).asList();
} catch (InstanceNotFoundException | ReflectionException ex) {
Logger.getLogger(PhysicalMemory.class.getName()).log(Level.SEVERE, null, ex);
al = Collections.emptyList();
}
for(Attribute a: al) {
System.out.println(a.getName()+": "+a.getValue());
}
}
}

This prints the values of the TotalPhysicalMemorySize, FreePhysicalMemorySize attributes, if they are available, regardless of how or in which package they are implemented. This still works in Java 9 where even attempts to access these sun-packages via Reflection are rejected.

On JREs not having these attributes, there is no platform independent way of getting them, but at least, this code doesn’t bail out with linkage errors but allows to proceed without the information.

Get available RAM in Java

This code snippet solves the problem.

public static long getAvailableMem()
{
String osName = System.getProperty("os.name");
if (osName.equals("Linux"))
{
try {
BufferedReader memInfo = new BufferedReader(new FileReader("/proc/meminfo"));
String line;
while ((line = memInfo.readLine()) != null)
{
if (line.startsWith("MemAvailable: "))
{
// Output is in KB which is close enough.
return java.lang.Long.parseLong(line.split("[^0-9]+")[1]) * 1024;
}
}
} catch (IOException e)
{
e.printStackTrace();
}
// We can also add checks for freebsd and sunos which have different ways of getting available memory
} else
{
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
com.sun.management.OperatingSystemMXBean sunOsBean = (com.sun.management.OperatingSystemMXBean)osBean;
return sunOsBean.getFreePhysicalMemorySize();
}
return -1;
}

How to free memory in Java?

Java uses managed memory, so the only way you can allocate memory is by using the new operator, and the only way you can deallocate memory is by relying on the garbage collector.

This memory management whitepaper (PDF) may help explain what's going on.

You can also call System.gc() to suggest that the garbage collector run immediately. However, the Java Runtime makes the final decision, not your code.

According to the Java documentation,

Calling the gc method suggests that
the Java Virtual Machine expend effort
toward recycling unused objects in
order to make the memory they
currently occupy available for quick
reuse. When control returns from the
method call, the Java Virtual Machine
has made a best effort to reclaim
space from all discarded objects.

Java confusion about Runtime.memory() vs. Windows' Physical Memory Usage History graph

SO THIS IS THE FINAL SOLUTION FOR JDK v1.8.0_181 (my case)

It turns out that all I needed to ad to my .bat was -XX:+UseG1GC switch, so my java.bat code now finally looks like this and it is doing exactly what I expect it to: releasing unused memory back to system:

@ECHO OFF
java -XX:+UseG1GC -Xmx8G -server -jar myapp %*
@if %errorlevel% neq 0 pause

The "funny" part is I knew about this switch but as I read somewhere that it is used as default GC collector by default - that is: no need to set it manually - so I ignored that option completely - I just did not check the part where it said FROM THE SPECIFIC VERSION OF JDK :-). Well...

Big thank you goes to @apangin for solution suggestion!



Related Topics



Leave a reply



Submit