性能问答>如何获得任意一段代码片段的内存消耗?>
5回复
2年前

如何获得任意一段代码片段的内存消耗?



我最近想对应用中特定算法的不同实现进行性能评估,包括时间消耗和内存消耗。其中,时间消耗比较容易获取,但是不知道怎么获取内存消耗。

目前,从网上找到了一篇文章介绍了一种方法,按照该方法,如果只需要获取整体消耗了多少内容,运行如下代码:

MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
MemoryUsage beforeHeapMemoryUsage = mbean.getHeapMemoryUsage();

Object instance = codeThatCreatesComplexDataStructure();

MemoryUsage afterHeapMemoryUsage = mbean.getHeapMemoryUsage();
long consumed = afterHeapMemoryUsage.getUsed() - 
                beforeHeapMemoryUsage.getUsed();
System.out.println("Total consumed Memory:" + consumed);

如果还需要获取具体的各种数据类型消耗的内存,运行如下代码:

public void memoryHistogram() {
    String name = ManagementFactory.getRuntimeMXBean().getName();
    String PID = name.substring(0, name.indexOf("@"));
    Process p = Runtime.getRuntime().exec("jcmd " + PID + " GC.class_histogram");
    try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
        input.lines().forEach(System.out::println);
    }
}

public void memoryAnalysis(){
    memoryHistogram();
    Object instance = codeThatCreatesComplexDataStructure();
    memoryHistogram();
}

但是,作者说这种方案不适用于多线程程序,因为内存消耗的变化可能是其他线程引起的,会导致结果不准确。然而,我要度量的应用确实是多线程的,不知道有没有大牛知道如何解决这个问题?
感激不尽!

具体方案描述点此链接

5053 阅读
请先登录,再评论

可以看看下面方法

com.sun.management.ThreadMXBean.getThreadAllocatedBytes()
22年前
回复 智晨:

类加载之后也会生成java.lang.Class对象

2年前回复
回复 LetUsJava:

大神好!我这两天测试了一下com.sun.management.ThreadMXBean.getThreadAllocatedBytes()这个方法,遇到了一些问题,请帮忙解答一下~
测试代码如下:

    ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean();
    long id = Thread.currentThread().getId();
//      new Long(0);
    long beforeMemUsage = threadMXBean.getThreadAllocatedBytes(id);
    long afterMemUsage = 0;

    {
        // put the code you want to measure here
        for (int i = 0; i < 10; i++) {
            new Long(i);
        }
    }

    afterMemUsage = threadMXBean.getThreadAllocatedBytes(id);
    System.out.println(afterMemUsage - beforeMemUsage);

主要是测试new Long(int)分配了多少内存,了解new背后的内存分配过程。我用不同的循环次数测试了一下,结果如下:

0 Long: 48 bytes
1 Long: 456 bytes
10 Long:    672 bytes
20 Long:    912 bytes
30 Long:    1152 bytes

其中,1和10,10和20,20和30之间的差异很容易解释,因为一个Long对象占用24 bytes,这个可以通过JOL验证。但是,我不太清楚0和1之间的差异为什么这么大?我感觉是因为类加载的问题,所以把第三行代码加上了,使得测试代码中不再进行类加载。重新运行测试,结果如下:

0 Long: 48 bytes
1 Long: 72 bytes
10 Long:    288 bytes
20 Long:    528 bytes
30 Long:    768 bytes

好像确实跟我想的一致。但是,类加载产生的类结构信息不是存储在Method Area中吗?Method Area不是Heap的一部分吧?com.sun.management.ThreadMXBean.getThreadAllocatedBytes()的JavaDoc中说这个方法只会返回heap中分配的空间,按理来说类加载不应该影响这个方法的返回结果?不知道我理解的对不对,请大神赐教!

2年前回复
回复 LetUsJava:

确实可以!

2年前回复

哈哈,说个题外话,感觉 Process p = Runtime.getRuntime().exec("jcmd " + PID + " GC.class_histogram"); 这样写不是很优雅,感觉可以这样写 👻

String name = ManagementFactory.getRuntimeMXBean().getName();
String pid = name.substring(0, name.indexOf("@"));
VirtualMachine vm = VirtualMachine.attach(pid);
InputStream in = ((HotSpotVirtualMachine) vm).heapHisto("-live");
try (BufferedReader input = new BufferedReader(new InputStreamReader(in))) {
      input.lines().forEach(System.out::println);
}
42年前