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

标签 java api memory runtime

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

答案here表示我可以这样做:

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

但这总是返回相同的数字,无论我运行哪个程序。例如,下面我有一个程序,无论我在 map 中输入多少数字,内存使用量都保持不变。

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;
    }
    
}

通过cambecc的main方法,输出为:

# 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

最佳答案

你做得对。获取内存使用的方法和你描述的完全一样:

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

但是你的程序总是返回相同的内存使用的原因是因为你没有创建足够的对象来克服 freeMemory 方法的精度限制。尽管它具有字节分辨率,但无法保证精确 freeMemory 需要达到何种程度。 javadoc 说了这么多:

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

尝试以下,创建两个 million 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());
    }
}

在我的机器上,我看到如下输出

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

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

-- 编辑--

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

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());
    }
}

在我的笔记本上,我看到以下输出。请注意,您的结果会因操作系统、硬件、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
...

从这些数据中有一些观察结果:

  1. 正如预期的那样,已用内存趋于增加。已用内存包括 Activity 对象和垃圾。
  2. 但在 GC 期间使用的内存减少,因为垃圾已被丢弃。例如,这发生在 #419680。
  3. 可用内存量按 block 减少,而不是按字节减少。 block 的大小不同。有时 block 非常小,例如 32 字节,但通常它们更大,例如 400K 或 800K。所以看起来 block 大小会有所不同。但与总堆大小相比,变化似乎很小。例如,在 #419681 处, block 大小仅为总堆大小的 0.6%。
  4. 正如预期的那样,可用内存趋于减少,直到 GC 启动并清理垃圾。发生这种情况时,可用内存会急剧增加,具体取决于丢弃的垃圾量。
  5. 这个测试会产生很多垃圾。随着 HashMap 大小的增加,它会重新散列其内容,从而产生大量垃圾。

关于java - 如何通过 Java 的 Runtime api 获取我的 Java 程序使用的内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17374743/

相关文章:

C++问题。关于类的容器和实例

iphone - 如何降低 CPU 使用率?

java - 编码 bat 字符串挑战未通过测试

java - 当struts.xml中配置action类时,请求的资源在struts2中不可用

angularjs - 单独的用户/管理员 API?

json - 验证 Node 中 swagger json.schema 的 POST 数据

java - jdk 1.8.0_91 中的 Accept-Encoding header 错误

java - 在抛出异常之前在 Catch 子句中调用时,JPA 存储库不执行更新

javascript - 无法正确访问 json github api 响应

iphone - iPhone 和 Android 上的内存对齐