如何通过Java的Runtime api获取我的Java程序使用的内存?

时间:2022-10-07 15:32:23

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?

有类似的问题,但他们似乎避免回答这个具体的问题。如何通过Java的Runtime api获取我的Java程序使用的内存?

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:

通过cambecc的主要方法,输出是:

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

2 个解决方案

#1


33  

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:

但是程序总是返回相同内存使用量的原因是因为您没有创建足够的对象来克服freeMemory方法的精度限制。虽然它具有字节分辨率,但无法保证freeMemory的精确程度。 javadoc说了很多:

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:

尝试以下操作,创建200万个NewObject实例,并在每次freeMemory更改结果时打印出来:

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).

请注意在实例化第21,437个对象之前,报告的可用内存是如何更改的?数字表明我正在使用的JVM(Java7 Win 64位)的freeMemory具有刚刚超过2.5MB的精度(尽管如果你运行实验,你会看到这个数字不同)。

-- 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.

此代码与上面相同,但打印有关内存使用情况的更多详细信息。希望JVM的内存使用情况更清晰一些。我们在循环中不断分配新对象。在每次迭代期间,如果totalMemory或freeMemory与上次迭代相同,我们不会打印任何内容。但如果其中一个发生了变化,我们会报告当前的内存使Δ值表示当前使用情况与先前存储器报告之间的差异。

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.:

在我的笔记本上,我看到以下输出。请注意,根据操作系统,硬件,JVM实现等,您的结果会有所不同:

#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. 正如所料,使用的内存往往会增加。使用的内存包括活动对象和垃圾。
  3. But used memory decreases during a GC, because garbage has been discarded. For example, this occurred at #419680.
  4. 但是在GC期间使用的内存会减少,因为垃圾已被丢弃。例如,这发生在#419680。
  5. 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.
  6. 可用内存量以块为单位减少,而不是逐字节减少。块大小不一。有时块很小,比如32字节,但通常它们更大,比如400K或800K。所以看起来块的大小会有所不同。但与总堆大小相比,变化看起来很小。例如,在#419681处,块大小仅为总堆大小的0.6%。
  7. 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.
  8. 正如预期的那样,可用内存往往会减少,直到GC启动并清除垃圾。发生这种情况时,可用内存会显着增加,具体取决于丢弃的垃圾量。
  9. This test generates a lot of garbage. As the hashmap grows in size, it rehashes its contents, thus generating a lot of garbage.
  10. 这个测试会产生大量垃圾。随着hashmap的大小增加,它会重新散列其内容,从而产生大量垃圾。

#2


1  

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:

如果你想重新计算到MiB我有:

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) + "%";
    }

#1


33  

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:

但是程序总是返回相同内存使用量的原因是因为您没有创建足够的对象来克服freeMemory方法的精度限制。虽然它具有字节分辨率,但无法保证freeMemory的精确程度。 javadoc说了很多:

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:

尝试以下操作,创建200万个NewObject实例,并在每次freeMemory更改结果时打印出来:

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).

请注意在实例化第21,437个对象之前,报告的可用内存是如何更改的?数字表明我正在使用的JVM(Java7 Win 64位)的freeMemory具有刚刚超过2.5MB的精度(尽管如果你运行实验,你会看到这个数字不同)。

-- 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.

此代码与上面相同,但打印有关内存使用情况的更多详细信息。希望JVM的内存使用情况更清晰一些。我们在循环中不断分配新对象。在每次迭代期间,如果totalMemory或freeMemory与上次迭代相同,我们不会打印任何内容。但如果其中一个发生了变化,我们会报告当前的内存使Δ值表示当前使用情况与先前存储器报告之间的差异。

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.:

在我的笔记本上,我看到以下输出。请注意,根据操作系统,硬件,JVM实现等,您的结果会有所不同:

#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. 正如所料,使用的内存往往会增加。使用的内存包括活动对象和垃圾。
  3. But used memory decreases during a GC, because garbage has been discarded. For example, this occurred at #419680.
  4. 但是在GC期间使用的内存会减少,因为垃圾已被丢弃。例如,这发生在#419680。
  5. 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.
  6. 可用内存量以块为单位减少,而不是逐字节减少。块大小不一。有时块很小,比如32字节,但通常它们更大,比如400K或800K。所以看起来块的大小会有所不同。但与总堆大小相比,变化看起来很小。例如,在#419681处,块大小仅为总堆大小的0.6%。
  7. 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.
  8. 正如预期的那样,可用内存往往会减少,直到GC启动并清除垃圾。发生这种情况时,可用内存会显着增加,具体取决于丢弃的垃圾量。
  9. This test generates a lot of garbage. As the hashmap grows in size, it rehashes its contents, thus generating a lot of garbage.
  10. 这个测试会产生大量垃圾。随着hashmap的大小增加,它会重新散列其内容,从而产生大量垃圾。

#2


1  

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:

如果你想重新计算到MiB我有:

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) + "%";
    }