简体   繁体   English

使用Runtime.getRuntime()。totalMemory()和freeMemory()计算内存的结果令人困惑?

[英]Confusing results on using Runtime.getRuntime().totalMemory() and freeMemory() to calculate memory?

I was using Runtime.getRuntime().totalMemory() and freeMemory() to calculate memory. 我正在使用Runtime.getRuntime().totalMemory()freeMemory()来计算内存。 However, I am confused by the results. 但是,我对结果感到困惑。

I already read the posts in: 我已经阅读了以下文章:

What are Runtime.getRuntime().totalMemory() and freeMemory()? 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?

a question on Runtime.getRuntime().totalMemory() 有关Runtime.getRuntime()。totalMemory()的问题

Here is my demo code: 这是我的演示代码:

package test;

class Memory

{

public static long used()

{

long total=Runtime.getRuntime().totalMemory();

long free=Runtime.getRuntime().freeMemory();

return (total-free);

}

}



package test;

import java.util.ArrayList;

public class MemTestQuestion {

    private static final long _10M = 10000000;

    public static void main(String[] args) {

        int runCount=10;
        for (int i = 0; i < runCount; i++) {
            arrayListMemTest();
        }
    }

    public static void arrayListMemTest()
    {
        long startTime = System.currentTimeMillis();
        long startMem=Memory.used();

        ArrayList<Integer>al= new ArrayList<Integer>();
        for (int i = 0; i < _10M; i++) {
            al.add(1000);
        }

        long endMem= Memory.used();
        long endEndTime = System.currentTimeMillis();

        long timeLast = endEndTime - startTime;
        long memUsed = endMem-startMem;


        System.out.print("lasts:"
                + timeLast + "ms = "+timeLast/1000.0+"s\t");
        System.out.println("mem used:"
                +memUsed+"bytes = "+new java.text.DecimalFormat("#.00").format(memUsed/(1024*1024.0))+"M");

        System.gc();
    }

}

Here are results when runCount=1(a variable located in Main method): 这是runCount = 1(位于Main方法中的变量)时的结果:

lasts:3606ms = 3.606s   mem used:214644488bytes = 204.70M

Here are results when runCount=10: 这是runCount = 10时的结果:

lasts:3643ms = 3.643s   mem used:214644488bytes = 204.70M
lasts:389ms = 0.389s    mem used:254054928bytes = 242.29M
lasts:366ms = 0.366s    mem used:219163424bytes = 209.01M
lasts:242ms = 0.242s    mem used:256265992bytes = 244.39M
lasts:222ms = 0.222s    mem used:255523768bytes = 243.69M
lasts:225ms = 0.225s    mem used:253843192bytes = 242.08M
lasts:253ms = 0.253s    mem used:253967736bytes = 242.20M
lasts:236ms = 0.236s    mem used:253994680bytes = 242.23M
lasts:234ms = 0.234s    mem used:254066232bytes = 242.30M
lasts:233ms = 0.233s    mem used:254091448bytes = 242.32M

What confused me most was that when the runCount=10 , the most results are around 240M. 最让我感到困惑的是,当runCount=10 ,大多数结果约为240M。 But when the runCount=1 , the results are around 200M. 但是,当runCount=1 ,结果约为200M。

I guess this is because the JAVA JVM does not collect garbage in time, so the first result are more convicing. 我猜这是因为JAVA JVM不能及时收集垃圾,所以第一个结果更令人信服。 Am I right? 我对吗? If not, can someone provide some clues or other suggestions? 如果没有,有人可以提供一些线索或其他建议吗? Thanks in advance. 提前致谢。

The purpose of the demo code was trying to compare the standard java containers and ones from third party. 演示代码的目的是尝试比较标准Java容器和第三方容器。

I guess this is because the JAVA JVM does not collect garbage in time, so the first result are more convicing. 我猜这是因为JAVA JVM不能及时收集垃圾,所以第一个结果更令人信服。 Am I right? 我对吗?

I don't think so. 我不这么认为。 Running System.gc() is supposed to be a hint to make "best effort" to collect all garbage. 运行System.gc()应该是“尽力而为”收集所有垃圾的提示。 Right now. 马上。 The hint can be ignored, but if the GC does run, you would expect it to do a >>full<< collection, and only return when that had completed. 该提示可以忽略,但是如果GC确实在运行,则您希望它执行>> full <<集合,并且仅在完成时返回。 There should be no question of the GC "keeping up". 毫无疑问,GC会“跟上”。

If not, can someone provide some clues or other suggestions? 如果没有,有人可以提供一些线索或其他建议吗?

It could be due to JIT compilation and other JVM warmup effects. 这可能是由于JIT编译和其他JVM预热效果所致。

When your application starts, your code's methods and all of the library methods that are used (transitively) are bytecodes, and will be interpreted by the bytecode interpreter. 当您的应用程序启动时,您的代码方法和(间接使用)的所有库方法都是字节码,并且将由字节码解释器进行解释。 As the bytecodes are interpreted, the JVM gathers stats on various things (eg to assist branch prediction). 在解释字节码时,JVM会收集各种事物的统计信息(例如,以帮助分支预测)。 Eventually, it decides to JIT compile the methods. 最终,它决定使用JIT编译方法。

So how does this relate to your observation? 那么这与您的观察有何关系?

  1. The stats are stored in the heap, and will be reachable ... until the JIT compiler "consumes" them. 这些统计信息存储在堆中,直到JIT编译器“使用”它们后才可以访问。

  2. The JIT compiler runs asynchronously, and when it is running it will also be using heap space to hold its data structures. JIT编译器异步运行,并且在运行时还将使用堆空间保存其数据结构。

  3. At startup, the JVM has to load classes, and this also consumes heap space to hold temporary objects. 在启动时,JVM必须加载类,并且这还会消耗堆空间来容纳临时对象。

  4. The heap size is not constant. 堆大小不是恒定的。 It will grow (and sometimes shrink) depending on how much free space is available after each GC cycle. 它会增长(有时会缩小),具体取决于每个GC周期后有多少可用空间。

All of these combine to give local "highs" and "lows" in heap sizes and heap space usage, during the JVM warmup phase. 在JVM预热阶段,所有这些结合在一起使堆大小和堆空间使用情况达到局部“高”和“低”。

I create the ArrayList as follows.. 我如下创建ArrayList

    ArrayList<Integer> al = new ArrayList<Integer>(){

        @Override
        protected void finalize() throws Throwable {
            super.finalize(); 
            System.out.println("finalize");
        }

    };

After I run your code, I realized that your confusing result did not happened because JVM didn't garbage collected. 运行代码后,我意识到您的混乱结果没有发生,因为JVM没有进行垃圾回收。 The result of the above code is as follows.. 以上代码的结果如下。

lasts:4875ms = 4.875s mem used:250675056bytes = 239.06M 持续时间:4875ms = 4.875s所使用的内存:250675056bytes = 239.06M
lasts:437ms = 0.437s mem used:278614888bytes = 265.71M 持续时间:437ms = 0.437s所用内存:278614888bytes = 265.71M
finalize 定案
finalize 定案
lasts:594ms = 0.594s mem used:252543848bytes = 240.84M 持续时间:594毫秒= 0.594毫秒使用的内存:252543848字节= 240.84M
finalize 定案
lasts:266ms = 0.266s mem used:277680536bytes = 264.82M 持续时间:266毫秒= 0.266毫秒使用的内存:277680536字节= 264.82M
finalize 定案
lasts:312ms = 0.312s mem used:280390568bytes = 267.40M 持续时间:312ms = 0.312s内存使用:280390568bytes = 267.40M
finalize 定案
lasts:297ms = 0.297s mem used:278106248bytes = 265.22M 持续时间:297毫秒= 0.297毫秒使用的内存:278106248字节= 265.22M
finalize 定案
lasts:297ms = 0.297s mem used:277852312bytes = 264.98M 持续时间:297毫秒= 0.297毫秒使用的内存:277852312字节= 264.98M
finalize 定案
lasts:312ms = 0.312s mem used:277885640bytes = 265.01M 持续时间:312ms = 0.312s mem使用:277885640bytes = 265.01M
finalize 定案
lasts:297ms = 0.297s mem used:277897448bytes = 265.02M 持续时间:297毫秒= 0.297毫秒使用的内存:277897448字节= 265.02M
finalize 定案
lasts:312ms = 0.312s mem used:277899896bytes = 265.03M 持续时间:312ms = 0.312s mem使用:277899896bytes = 265.03M
finalize 定案

In this code also, first time it gets run, it shows a value ( 239.06M ) less than the average value. 同样在此代码中,第一次运行时,它显示的值( 239.06M )小于平均值。

Then I change the main method 然后我改变main method

public static void main(String[] args) throws InterruptedException {
    Thread.sleep(7000);
    int runCount = 10;
    for (int i = 0; i < runCount; i++) {
        arrayListMemTest();
    }
}

And the result was: 结果是:

lasts:2640ms = 2.64s mem used:275635104bytes = 262.87M 持续时间:2640ms = 2.64s mem used:275635104bytes = 262.87M
lasts:624ms = 0.624s mem used:253167968bytes = 241.44M 持续时间:624毫秒= 0.624毫秒使用的内存:253167968字节= 241.44M
finalize 定案
finalize 定案
lasts:411ms = 0.411s mem used:303362328bytes = 289.31M 持续时间:411ms = 0.411s mem使用:303362328bytes = 289.31M
finalize 定案
lasts:527ms = 0.527s mem used:253055288bytes = 241.33M 持续时间:527毫秒= 0.527毫秒使用的内存:253055288字节= 241.33M
finalize 定案
lasts:329ms = 0.329s mem used:280855512bytes = 267.84M 持续时间:329ms = 0.329s mem used:280855512bytes = 267.84M
finalize 定案
lasts:340ms = 0.34s mem used:252478904bytes = 240.78M 持续时间:340毫秒= 0.34毫秒使用的内存:252478904字节= 240.78M
finalize 定案
lasts:350ms = 0.35s mem used:277791960bytes = 264.92M 持续时间:350ms = 0.35s mem使用:277791960bytes = 264.92M
finalize 定案
lasts:312ms = 0.312s mem used:277059280bytes = 264.22M 持续时间:312ms = 0.312s mem使用:277059280bytes = 264.22M
finalize 定案
lasts:314ms = 0.314s mem used:279330968bytes = 266.39M 持续时间:314ms = 0.314s mem使用:279330968bytes = 266.39M
finalize 定案
lasts:376ms = 0.376s mem used:278511960bytes = 265.61M 持续时间:376ms = 0.376s mem used:278511960bytes = 265.61M
finalize 定案

And this time, it shows a average value around 260M . 而这一次,它显示了大约260M的平均值。

As I think, JVM needs some time to fully load its libraries. 我认为, JVM需要一些时间来完全加载其库。 The programs get run before initializing JVM fully.. 在完全初始化JVM之前先运行程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM