Runtime.getRuntime().totalMemory()和freeMemory()是什么?它们与编程有关。

182
7个回答

305

名称和值很容易混淆。如果你正在寻找总可用内存,你需要自己计算这个值。 不是通过调用freeMemory();方法所得到的。

参考以下指南:

总分配内存,它等于配置的-Xmx值:

Runtime.getRuntime().maxMemory();

当前已分配的空闲内存是当前分配给新对象的可用空间。注意,这不是总可用内存:

Runtime.getRuntime().freeMemory();

总分配的内存是为Java进程保留的总空间:

Runtime.getRuntime().totalMemory();

已使用内存需要计算:

usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

总可用内存需要计算:

freeMemory = Runtime.getRuntime().maxMemory() - usedMemory;

图片可能有助于澄清:

java runtime memory


3
这与 Debug.getMemoryInfo() 不同吗? - IgorGanapolsky
1
注意:已使用的内存可能包含下一个GC将清除的不再引用的对象。 - Gab是好人
@cheneym,只有在机器中可用“Xmx-Usedmemory”时,Java字节码指令才会被处理器处理,从而占用免费和未定位的内存。 Xmx就像气球的最大容量,它可以从机器本身获得空气,一旦获得空气,它就会被填满,并在超过Xmx限制时爆炸。但是,总免费内存不会告诉JVM机器中实际可用的内存,而只是数字。是否有任何方法可以找出机器中实际可用的内存,以便我可以知道JVM剩余进程是否有所需内存可用? - Maria

214

根据API文档所述

totalMemory()

返回Java虚拟机中的总内存量。该方法返回的值可能会随时间和主机环境的不同而变化。 请注意,持有任何给定类型对象所需的内存量可能取决于具体实现。

maxMemory()

返回Java虚拟机尝试使用的最大内存量。如果没有固有限制,则返回Long.MAX_VALUE的值。

freeMemory()

返回Java虚拟机中的可用内存量。调用gc方法可能会导致freeMemory返回值增加。

关于你的问题,maxMemory() 返回 -Xmx 的值。

你可能想知道为什么有 totalMemory()maxMemory()。答案是JVM懒惰地分配内存。假设你这样启动Java进程:

java -Xms64m -Xmx1024m Foo

你的进程从64MB内存开始,如果需要更多(最多1024MB),它将分配内存。 totalMemory() 对应于JVM当前可用于Foo的内存量。如果JVM需要更多内存,则会惰性地分配它,直到达到最大内存限制。如果您使用-Xms1024m -Xmx1024m运行,则从totalMemory()maxMemory()获得的值将相等。

另外,如果您想准确地计算已使用的内存量,请使用以下公式:

final long usedMem = totalMemory() - freeMemory();

“-Xmx”值似乎直接影响初始的“maxMemory()”值,但我发现在程序运行时报告的“maxMemory()”可能会略微增加约1%。 - H2ONaCl
2
这与 Debug.getNativeHeapFreeSize() 有何不同? - IgorGanapolsky
@H2ONaCl 是的,它可能会稍微改变,因为JVM的UseAdaptiveSizePolicy默认是启用的。顺便说一下:maxMemory() = Xmx - 单个survivor空间的大小。为什么?因为同时只能使用一个survivor空间。 - G. Demecki

14

为更好地理解,请运行以下程序(在jdk1.7.x中):

$ java -Xms1025k -Xmx1025k -XshowSettings:vm  MemoryTest
这将打印出jvm选项以及jvm中可用的已使用自由总共最大内存。
public class MemoryTest {    
    public static void main(String args[]) {
                System.out.println("Used Memory   :  " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) + " bytes");
                System.out.println("Free Memory   : " + Runtime.getRuntime().freeMemory() + " bytes");
                System.out.println("Total Memory  : " + Runtime.getRuntime().totalMemory() + " bytes");
                System.out.println("Max Memory    : " + Runtime.getRuntime().maxMemory() + " bytes");            
        }
}

11

其他答案的编码版本(截至撰写本文时):

import java.io.*;

/**
 * This class is based on <a href="http://stackoverflow.com/users/2478930/cheneym">cheneym</a>'s
 * <a href="https://dev59.com/FHA65IYBdhLWcg3w5zHB#18375641">awesome interpretation</a>
 * of the Java {@link Runtime}'s memory query methods, which reflects intuitive thinking.
 * Also includes comments and observations from others on the same question, and my own experience.
 * <p>
 * <img src="https://istack.dev59.com/GjuwM.webp" alt="Runtime's memory interpretation">
 * <p>
 * <b>JVM memory management crash course</b>:
 * Java virtual machine process' heap size is bounded by the maximum memory allowed.
 * The startup and maximum size can be configured by JVM arguments.
 * JVMs don't allocate the maximum memory on startup as the program running may never require that.
 * This is to be a good player and not waste system resources unnecessarily.
 * Instead they allocate some memory and then grow when new allocations require it.
 * The garbage collector will be run at times to clean up unused objects to prevent this growing.
 * Many parameters of this management such as when to grow/shrink or which GC to use
 * can be tuned via advanced configuration parameters on JVM startup.
 *
 * @see <a href="https://dev59.com/FHA65IYBdhLWcg3w5zHB#42567450">
 *     What are Runtime.getRuntime().totalMemory() and freeMemory()?</a>
 * @see <a href="http://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf">
 *     Memory Management in the Sun Java HotSpot™ Virtual Machine</a>
 * @see <a href="http://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html">
 *     Full VM options reference for Windows</a>
 * @see <a href="http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html">
 *     Full VM options reference for Linux, Mac OS X and Solaris</a>
 * @see <a href="http://www.oracle.com/technetwork/articles/java/vmoptions-jsp-140102.html">
 *     Java HotSpot VM Options quick reference</a>
 */
public class SystemMemory {

    // can be white-box mocked for testing
    private final Runtime runtime = Runtime.getRuntime();

    /**
     * <b>Total allocated memory</b>: space currently reserved for the JVM heap within the process.
     * <p>
     * <i>Caution</i>: this is not the total memory, the JVM may grow the heap for new allocations.
     */
    public long getAllocatedTotal() {
        return runtime.totalMemory();
    }

    /**
     * <b>Current allocated free memory</b>: space immediately ready for new objects.
     * <p>
     * <i>Caution</i>: this is not the total free available memory,
     * the JVM may grow the heap for new allocations.
     */
    public long getAllocatedFree() {
        return runtime.freeMemory();
    }

    /**
     * <b>Used memory</b>:
     * Java heap currently used by instantiated objects. 
     * <p>
     * <i>Caution</i>: May include no longer referenced objects, soft references, etc.
     * that will be swept away by the next garbage collection.
     */
    public long getUsed() {
        return getAllocatedTotal() - getAllocatedFree();
    }

    /**
     * <b>Maximum allocation</b>: the process' allocated memory will not grow any further.
     * <p>
     * <i>Caution</i>: This may change over time, do not cache it!
     * There are some JVMs / garbage collectors that can shrink the allocated process memory.
     * <p>
     * <i>Caution</i>: If this is true, the JVM will likely run GC more often.
     */
    public boolean isAtMaximumAllocation() {
        return getAllocatedTotal() == getTotal();
        // = return getUnallocated() == 0;
    }

    /**
     * <b>Unallocated memory</b>: amount of space the process' heap can grow.
     */
    public long getUnallocated() {
        return getTotal() - getAllocatedTotal();
    }
    
    /**
     * <b>Total designated memory</b>: this will equal the configured {@code -Xmx} value.
     * <p>
     * <i>Caution</i>: You can never allocate more memory than this, unless you use native code.
     */
    public long getTotal() {
        return runtime.maxMemory();
    }

    /**
     * <b>Total free memory</b>: memory available for new Objects,
     * even at the cost of growing the allocated memory of the process.
     */
    public long getFree() {
        return getTotal() - getUsed();
        // = return getAllocatedFree() + getUnallocated();
    }

    /**
     * <b>Unbounded memory</b>: there is no inherent limit on free memory.
     */
    public boolean isBounded() {
        return getTotal() != Long.MAX_VALUE;
    }

    /**
     * Dump of the current state for debugging or understanding the memory divisions.
     * <p>
     * <i>Caution</i>: Numbers may not match up exactly as state may change during the call.
     */
    public String getCurrentStats() {
        StringWriter backing = new StringWriter();
        PrintWriter out = new PrintWriter(backing, false);
        out.printf("Total: allocated %,d (%.1f%%) out of possible %,d; %s, %s %,d%n",
                getAllocatedTotal(),
                (float)getAllocatedTotal() / (float)getTotal() * 100,
                getTotal(),
                isBounded()? "bounded" : "unbounded",
                isAtMaximumAllocation()? "maxed out" : "can grow",
                getUnallocated()
        );
        out.printf("Used: %,d; %.1f%% of total (%,d); %.1f%% of allocated (%,d)%n",
                getUsed(),
                (float)getUsed() / (float)getTotal() * 100,
                getTotal(),
                (float)getUsed() / (float)getAllocatedTotal() * 100,
                getAllocatedTotal()
        );
        out.printf("Free: %,d (%.1f%%) out of %,d total; %,d (%.1f%%) out of %,d allocated%n",
                getFree(),
                (float)getFree() / (float)getTotal() * 100,
                getTotal(),
                getAllocatedFree(),
                (float)getAllocatedFree() / (float)getAllocatedTotal() * 100,
                getAllocatedTotal()
        );
        out.flush();
        return backing.toString();
    }

    public static void main(String... args) {
        SystemMemory memory = new SystemMemory();
        System.out.println(memory.getCurrentStats());
    }
}

示例输出:

Total: allocated 33,554,432 (12.5%) out of possible 268,435,456; bounded, can grow 234,881,024
Used: 2,392,832; 0.9% of total (268,435,456); 7.1% of allocated (33,554,432)
Free: 266,042,624 (99.1%) out of 268,435,456 total; 31,161,600 (92.9%) out of 33,554,432 allocated

8

Runtime#totalMemory - 这是JVM到目前为止分配的内存。这不一定是正在使用的或最大的内存。

Runtime#maxMemory - JVM配置使用的最大内存量。一旦您的进程达到此数量,JVM将不会分配更多内存,而是更频繁地进行垃圾回收。

Runtime#freeMemory - 我不确定这是从最大值还是未使用部分的总数度量的。我猜测这是未使用总数的度量。


7

JVM堆大小可以通过垃圾回收机制进行扩展和收缩。 但是,它不能超过最大内存大小:Runtime.maxMemory。这就是最大内存的含义。总内存表示分配的堆大小。空闲内存表示总内存中可用的大小。

例如)java -Xms20M -Xmn10M -Xmx50M 〜〜〜。 这意味着jvm应该在启动时(ms)分配20M堆。在这种情况下,总内存为20M。空闲内存为20M减去已使用的大小。如果需要更多堆,则JVM会分配更多,但不能超过50M(mx)。在最大值的情况下,总内存为50M,空闲大小为50M减去已使用的大小。至于最小大小(mn),如果堆没有被大量使用,jvm可以将堆大小缩小到10M。

这种机制是为了提高内存效率。如果小型Java程序在巨大的固定大小堆内存上运行,那么可能会浪费大量内存。


4

您可以看到以MB格式的结果,分成1024 x 1024,等同于1 MB

int dataSize = 1024 * 1024;

System.out.println("Used Memory   : " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/dataSize + " MB");
System.out.println("Free Memory   : " + Runtime.getRuntime().freeMemory()/dataSize + " MB");
System.out.println("Total Memory  : " + Runtime.getRuntime().totalMemory()/dataSize + " MB");
System.out.println("Max Memory    : " + Runtime.getRuntime().maxMemory()/dataSize + " MB");  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接