JDK 8

-XX:+PrintGCDetails

The heap layout and usage will be printed at VM exit:

 PSYoungGen      total 443904K, used 283177K [0x00000000dcc00000, 0x00000000f7f00000, 0x0000000100000000)
  eden space 442368K, 63% used [0x00000000dcc00000,0x00000000ee07a6f8,0x00000000f7c00000)
  from space 1536K, 4% used [0x00000000f7d80000,0x00000000f7d90000,0x00000000f7f00000)
  to   space 1536K, 0% used [0x00000000f7c00000,0x00000000f7c00000,0x00000000f7d80000)
 ParOldGen       total 72704K, used 980K [0x0000000096400000, 0x000000009ab00000, 0x00000000dcc00000)
  object space 72704K, 1% used [0x0000000096400000,0x00000000964f5060,0x000000009ab00000)
 Metaspace       used 4568K, capacity 4718K, committed 4992K, reserved 1056768K
  class space    used 472K, capacity 532K, committed 640K, reserved 1048576K

JDK 9+

-Xlog:gc+heap+exit

[9.405s][info][gc,heap,exit]  garbage-first heap   total 276480K, used 149668K [0x0000000700000000, 0x0000000800000000)
[9.405s][info][gc,heap,exit]   region size 1024K, 147 young (150528K), 1 survivors (1024K)
[9.405s][info][gc,heap,exit]  Metaspace       used 6335K, capacity 6395K, committed 6784K, reserved 1056768K
[9.405s][info][gc,heap,exit]   class space    used 511K, capacity 530K, committed 640K, reserved 1048576K

total is the committed memory; the range [0x0000000700000000, 0x0000000800000000) is the reserved space.

If you want to print heap at every GC rather than at VM exit, use -XX:+PrintHeapAtGC in JDK 8, or -Xlog:gc+heap=debug in JDK 9+.

Answer from apangin on Stack Overflow
🌐
Heroku Dev Center
devcenter.heroku.com › articles › java-memory-issues
Troubleshooting Memory Issues in Java Applications | Heroku Dev Center
April 17, 2026 - If your app is experiencing high levels of native memory usage (i.e., the difference between Total RSS and JVM Heap), then you might need to configure your application to print Native Memory Tracking information when it shuts down. To do so, set the following configuration variable: $ heroku config:set JAVA_OPTS="-XX:NativeMemoryTracking=detail -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics"
Discussions

How can I find Java heap size and memory used (Linux)? - Stack Overflow
A better explanation of what Virtual memory really used by a Java process is discussed here. ... Save this answer. ... Show activity on this post. ... However, if -Xms or -Xmx is absent for the Java process you are interested in, it means your Java process is using the default heap sizes. You can use the following command to find out the default sizes. java -XX:+PrintFlagsFinal ... More on stackoverflow.com
🌐 stackoverflow.com
How to print out Java jvm memory use for debugging purposes? - Stack Overflow
I have a large Java app that uses massive amounts of memory at times and I would like to monitor this at intervals to see if heap or permgen limits are reached or have gotten near to them. How can I print out the most relevant pieces of mem usage information using java? More on stackoverflow.com
🌐 stackoverflow.com
Windows Java command line to know the memory settings - Stack Overflow
I'm looking for a windows command line that would tell me at how much the memory settings have been set to start an application. What is the default for Windows 7 JRE 1.6 if I do "java -jar my.jar... More on stackoverflow.com
🌐 stackoverflow.com
How can I get the memory that my Java program uses via Java's Runtime API? - Stack Overflow
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: More on stackoverflow.com
🌐 stackoverflow.com
🌐
Oracle
docs.oracle.com › cd › E27119_01 › doc.11113 › e23127 › systemadministratorsguideprintable23.html
Tuning the Java Runtime Environment - Oracle Identity Analytics System Administrator's Guide
This section discusses topics related to tuning the Java heap size for performance: ... Maximum heap size depends on the maximum address space per process. Maximum heap space is always smaller than the maximum address space per process because the process also needs space for stack, libraries, and so on. To determine the maximum heap space that can be allocated, use a profiling tool to examine the way memory is used.
🌐
TheServerSide
theserverside.com › blog › Coffee-Talk-Java-News-Stories-and-Opinions › jvm-options-java-parameters-command-line-environment-variable-list-xms-xmx-memory
Critical Java JVM options and parameters
August 19, 2025 - -XX:+PrintFlagsFinal -version prints out all of the currently configured flags and options set on your JVM. To use any of these JVM options, simply append them as text after the java runtime command.
🌐
Mkyong
mkyong.com › home › java › find out your java heap memory size
Find out your Java heap memory size - Mkyong.com
March 9, 2014 - -XX:MaxPermSize<size> - Set the maximum PermGen Size. $ java -XX:PermSize=64m -XX:MaxPermSize=128m JavaApp · 3. Java Stack Size Size of a Java thread. If a project has a lot of threads processing, try reduce this stack size to avoid running out of memory. -Xss ... Note The default value for heap size, perm gen, or stack size is differ from different JVMs. The best practice is always defining your own value. ... $ java -XX:+PrintFlagsFinal -version | grep -iE 'HeapSize|PermSize|ThreadStackSize' uintx InitialHeapSize := 64781184 {product} uintx MaxHeapSize := 1038090240 {product} uintx PermSize = 21757952 {pd product} uintx MaxPermSize = 174063616 {pd product} intx ThreadStackSize = 1024 {pd product} java version "1.7.0_51" OpenJDK Runtime Environment (IcedTea 2.4.4) (7u51-2.4.4-0ubuntu0.13.10.1) OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)
🌐
Baeldung
baeldung.com › home › java › jvm › command-line tools to find the java heap size
Command-Line Tools to Find the Java Heap Size | Baeldung
January 27, 2024 - So, there is no way to know about, say, the current committed memory. In addition to jps, a few other tools will report the same thing. For example, the “jcmd <pid> VM.command_line” will also report these details: $ jcmd 4309 VM.command_line 4309: VM Arguments: jvm_args: -Xms200m -Xmx1g java_command: quarkus.jar java_class_path (initial): quarkus.jar Launcher Type: SUN_STANDARD
🌐
Bell Software
bell-sw.com › blog › guide-to-jvm-memory-configuration-options
List of JVM memory configuration flags
March 14, 2024 - Before adjusting garbage collector settings, learn to understand its behavior. GC logs are text files that provide exhaustive information about GC work: total GC time, memory reclamation and allocation, etc. Note that GC logging parameters vary between Java 8 and Java 9+: -XX:+PrintGCDetails and -Xlog:gc in Java 9+ substitute -XX:PrintGC in Java 8;
Find elsewhere
🌐
Oracle
docs.oracle.com › cd › E15289_01 › JRPTG › tune_footprint.htm
9 Tuning for a Small Memory Footprint
To determine how the memory usage of the JVM process is distributed, request a memory analysis by using the jrcmd command to print the memory usage of the JVM. For more information, see Oracle JRockit JDK Tools. After you get information about the memory usage of the JVM, you can start tuning ...
🌐
Vogella
vogella.com › tutorials › JavaPerformance › article.html
Java Performance - Memory and Runtime Analysis - Tutorial
May 31, 2026 - This article will be a collection of Java performance measurement pointer. It describes how memory works in general and how Java use the heap and the stack. The article describes how to set the available memory for Java.
🌐
Viralpatel
viralpatel.net › getting-jvm-heap-size-used-memory-total-memory-using-java-runtime
Getting Java JVM heap size, used memory, total memory using Java Runtime
May 19, 2009 - Following is the small example of getting/reading JVM Heap Size, Total Memory and used memory using Java Runtime api. /** * Class: TestMemory * @author: Viral Patel * @description: Prints JVM memory utilization statistics */ public class TestMemory { public static void main(String [] args) { int mb = 1024*1024; //Getting the runtime reference from system Runtime runtime = Runtime.getRuntime(); System.out.println("##### Heap utilization statistics [MB] #####"); //Print used memory System.out.println("Used Memory:" + (runtime.totalMemory() - runtime.freeMemory()) / mb); //Print free memory Syste
🌐
Tibco
docs.tibco.com › pub › str › latest › doc › html › admin › java-vmargs.html
Java VM Memory Settings
Unlike previous releases, StreamBase releases starting with 10.0 do not have internally set default Java VM memory settings for fragments and nodes. In the absence of explicit settings, the default values used are those of the JVM engine that runs the fragment. You can determine your JVM's defaults with a command like the following for macOS and Linux: java -XX:+PrintFlagsFinal -version | grep HeapSize
🌐
Apache
geode.apache.org › docs › guide › 114 › managing › monitor_tune › system_member_performance_jvm_mem_settings.html
JVM Memory Settings and System Performance | Geode Docs
The following parameter added to the Java application startup limits the maximum size of the stack. ... In particular, we recommend starting the cache servers with a stack size of 384k or 512k in such cases. For example: gfsh>start server --name=server-name --J=-Xss384k gfsh>start server --name=server-name --J=-Xss512k · Off-heap memory size—For applications that use off-heap memory, specifies how much off-heap memory to allocate. Setting off-heap-memory-size is prerequisite to enabling the off-heap capability for individual regions.
🌐
Oracle
docs.oracle.com › cd › E13150_01 › jrockit_jvm › jrockit › geninfo › diagnos › tune_footprint.html
Tuning For a Small Memory Footprint
To determine how the memory usage of the JVM process is distributed, you can request a memory analysis by using jrcmd to print the JVM’s memory usage. See Using jrcmd and Available Diagnostic Commands for more information. When you have acquired information on the JVM’s memory usage you can start tuning the JVM to reduce the memory footprint within the areas that use the most memory. The most obvious place to start tuning the memory footprint is the Java heap size.
Top answer
1 of 6
118

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.
2 of 6
4

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) + "%";
    }
Top answer
1 of 3
2

Show the command line column in task manager, which should have the settings assuming they were passed on the command line:

2 of 3
2

Note that the below answer might require JMX to be enabled - I believe it's disabled by default in public JRE, and requires a restart of the JVM to change it, which would be rather useless in your case. Still, worth a shot?

Whether they work or not will also depend on the specific native wrapping method used by this application.

You should be able to copy these tools along with jli.dll from a JDK with a matching minor version and architecture to the JRE you're running. You could also run the tools remotely, though that's less likely to work without some initial set up due to the security requirements for remote connections.


The jps tool, available in the JDK, should be able to provide this information with the command jps -v. If you have multiple Java processes running, you can identify them by the PID in the first column.

Example output on Netbeans (PID 9056) (which uses a native wrapper similar to your application):

9056  -Dnetbeans.importclass=org.netbeans.upgrade.AutoUpgrade -Dnetbeans.accept_
license_class=org.netbeans.license.AcceptLicense -client -Xss2m -Xms32m -XX:Perm
Size=32m -Dapple.laf.useScreenMenuBar=true -Dapple.awt.graphics.UseQuartz=true -
Dsun.java2d.noddraw=true -Dsun.java2d.dpiaware=true -Dsun.zip.disableMemoryMappi
ng=true -Xmx1024m <snip>

Notice the -Xss, -Xms and -Xmx arguments.


Another thing you can try is jinfo, which allows you to target a specific PID, e.g. jinfo 9056.

NOTE - This utility is unsupported and may or may not be available in future versions of the JDK.


You could also try jconsole and jvisualvm, though they seem to have trouble attaching to wrapped JVMs and listing VM arguments, from my testing.