【问题标题】:How can I get the memory that my Java program uses via Java's Runtime api?如何通过 Java 的 Runtime api 获取我的 Java 程序使用的内存?
【发布时间】:2013-06-26 19:39:57
【问题描述】:

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

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

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

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

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,总计:128516096,免费:127173744,差异:671120

173579,总计:128516096,免费:110033976,差异:671128

335207,总计:128516096,免费:92417792,差异:637544

672788,总计:224198656,免费:159302960,差异:1221520

1171480,总计:224198656,免费:106939136,差异:1221544

1489771,总计:368377856,免费:227374816,差异:1212984

1998743,总计:368377856,免费:182494408,差异:1212984

【问题讨论】:

  • getRuntime().totalMemory() 指的是 Java 运行时可用的内存总量,而不是具体的应用程序可用的内存量。
  • @Robert Harvey 我已编辑。由于某种原因,提供的答案对我不起作用...
  • 我重新打开了,但也许你误解了我的意思。 getRuntime().totalMemory 不会为你想做的事情工作
  • 谢谢。是的我明白。我上面引用的答案似乎表明它会。
  • @RobertHarvey:澄清一下,你为什么说totalMemory 不适合@MattB?按照目前的措辞来看这个问题,@MattB 想要测量在单个 JVM 实例上运行的 Java 程序的内存使用情况。对于这种情况,totalMemory - freeMemory 是正确的。 totalMemory 的结果不受在同一台机器上运行的其他 JVM 实例(即进程)的影响。并且@MattB 没有提及在与他的程序相同的 JVM 实例中运行的其他程序。 totalMemory 怎么不对?

标签: java api memory runtime


【解决方案1】:

公平的免费内存:

maxMemory() - totalMemory() + freeMemory()

它将与以下内容相同:

maxMemory() - (used memory), where (used memory) = totalMemory() - freeMemory()

因为freeMemory() 只为您提供totalMemory() 内的可用内存,但totalMemory() 仍然可以增长到maxMemory()

【讨论】:

    【解决方案2】:
    public static void memoryStats() {
        int mb = 1024 * 1024;
        // get Runtime instance
        Runtime instance = Runtime.getRuntime();
        System.out.println("***** Heap utilization statistics [MB] *****\n");
        // available memory
        System.out.println("Total Memory: " + instance.totalMemory() / mb);
        // free memory
        System.out.println("Free Memory: " + instance.freeMemory() / mb);
        // used memory
        System.out.println("Used Memory: "
                + (instance.totalMemory() - instance.freeMemory()) / mb);
        // Maximum available memory
        System.out.println("Max Memory: " + instance.maxMemory() / mb);
    }
    

    参考号:Here

    【讨论】:

      【解决方案3】:

      以下可用于您的 java 方法中以获取与内存相关的统计信息。

      // Get the Java runtime
      Runtime runtime = Runtime.getRuntime();
      // Run the garbage collector
      runtime.gc();
      // Calculate the used memory
      long memory = runtime.totalMemory() - runtime.freeMemory();
      System.out.println("Used memory is bytes: " + memory);
      System.out.println("Used memory is megabytes: "
      + bytesToMegabytes(memory));
      

      【讨论】:

      • runtime.gc(); 几乎是不必要的。它只是提示 JRE 启动 GC。但是是否应该启动 GC 取决于 JRE。
      【解决方案4】:

      我有以下方法

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

      以字节为单位返回(使用的)内存。

      如果你想重新计算为 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) + "%";
          }
      

      【讨论】:

        【解决方案5】:

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

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

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

        当前可用于未来分配的对象的内存总量的近似值,以字节为单位。

        尝试以下操作,这将创建两个 百万 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. 正如预期的那样,已用内存趋于增加。已用内存包括活动对象和垃圾。
        2. 但在 GC 期间使用的内存减少,因为垃圾已被丢弃。例如,这发生在 #419680。
        3. 可用内存量以块的形式减少,而不是逐字节减少。块的大小不同。有时块非常小,例如 32 字节,但通常它们更大,例如 400K 或 800K。所以看起来块大小会有所不同。但与总堆大小相比,变化似乎很小。例如,在 #419681 处,块大小仅为总堆大小的 0.6%。
        4. 正如预期的那样,可用内存趋于减少,直到 GC 启动并清理垃圾。发生这种情况时,可用内存会急剧增加,具体取决于丢弃的垃圾量。
        5. 此测试会产生大量垃圾。随着哈希图大小的增加,它会重新散列其内容,从而产生大量垃圾。

        【讨论】:

        • 感谢您的回复 cambecc。这让我对我的问题有了一些了解,尽管如果我向 NewObject 类添加更多字段(例如更多 NewObject),我应该能够看到我的程序增加了内存,但是我得到了堆栈溢出。运行您的主要方法,我得到以下内容,这似乎非常不可靠,因为它再次从多到少波动......(我已经添加到主要问题中,因为评论的格式很混乱)跨度>
        • 另外,为什么 diff 没有改变(在你的,以及我的原始问题的一部分)?我认为这就是重点。
        • 您正在目睹垃圾收集器在工作(不知道为什么会出现堆栈溢出--听起来像是测试中的错误)。 JVM 的堆以较小的大小 (128MB) 开始为空。随着越来越多的对象被创建,堆变得越来越满。最终堆变得如此满,以至于触发了一个完整的 GC,导致两个影响:1)堆大小增加到 224MB,从而增加了totalMemory,2)垃圾被收集,增加了@987654335 @。都很正常。看来您的 JVM 会根据...谁知道,更改其 freeMemory 的精度。再次,行为符合规范。
        • 顺便说一句,Diff 不是已用内存量。它是对freeMemory的精度的度量,即先前报告的freeMemory的数量与当前报告的数量之间的差异。您会期望它保持相当恒定,因为 freeMemory 的精度会发生混乱变化,这很奇怪。
        • 你刚刚用 2_000_000 让我大吃一惊,这是我第一次看到使用 _ 功能但不知道这是一个东西
        猜你喜欢
        • 2021-07-03
        • 1970-01-01
        • 1970-01-01
        • 2019-11-20
        • 1970-01-01
        • 1970-01-01
        • 2014-07-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多