java - ThreadMXBean.getThreadAllocationBytes(long id) 中包含哪些 JVM 运行时数据区域

标签 java memory-management jvm

我试图获取一些代码片段的内存消耗。经过一番搜索,我意识到可以使用 ThreadMXBean.getThreadAllocationBytes(long id) 来实现这一点。所以我用下面的代码测试了这个方法:

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

我在 for 循环中使用不同的迭代时间(0、1、10、20 和 30)运行此代码。结果如下:

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字节。但0和1之间的巨大差异让我感到困惑。 实际上,我猜这是由类加载引起的。所以我取消了第3行代码的注释,结果如下:

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

看来结果证实了我的猜测。然而,在我看来,类结构的信息存储在方法区中,它不属于堆内存的一部分。正如 ThreadMXBean.getThreadAllocatedBytes(long id) 的 Javadoc 所示,它返回堆内存中分配的内存总量。我错过了什么吗?

测试的JVM版本是:

java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

谢谢!

最佳答案

第一次调用new Long(0)会导致解析new字节码引用的常量池条目。第一次解析 CONSTANT_Class_info 时,JVM 会加载引用的类 - java.lang.Long

ClassLoader.loadClass是用Java实现的,当然可以分配Java对象。例如,getClassLoadingLock方法在parallelLockMap中创建一个新的锁对象和一个新条目:

    protected Object getClassLoadingLock(String className) {
        Object lock = this;
        if (parallelLockMap != null) {
            Object newLock = new Object();
            lock = parallelLockMap.putIfAbsent(className, newLock);
            if (lock == null) {
                lock = newLock;
            }
        }
        return lock;
    }

此外,当在系统字典中查找类名时,JVM 会创建一个新的 String 对象。

我用了async-profiler记录 JVM 在加载 java.lang.Long 类时执行的所有堆分配。这是可点击的交互式火焰图:

Allocation Flame Graph

该图包括 13 个样本 - 每个分配的对象一个。分配的对象的类型未显示,但可以从上下文(堆栈跟踪)轻松猜测。

  • 绿色表示 Java 堆栈跟踪;
  • 黄色表示虚拟机堆栈跟踪。

请注意,每个 java_lang_String::basic_create() (和类似的)分配两个对象:一个 java.lang.String 实例及其支持 char[ ]数组。

该图由以下测试程序生成:

import one.profiler.AsyncProfiler;

public class ProfileHeapAlloc {

    public static void main(String[] args) throws Exception {
        AsyncProfiler profiler = AsyncProfiler.getInstance();

        // Dry run to skip allocations caused by AsyncProfiler initialization
        profiler.start("_ZN13SharedRuntime19dtrace_object_allocEP7oopDesci", 0);
        profiler.stop();

        // Real profiling session
        profiler.start("_ZN13SharedRuntime19dtrace_object_allocEP7oopDesci", 0);

        new Long(0);

        profiler.stop();
        profiler.execute("file=alloc.svg");
    }
}

如何运行:

java -Djava.library.path=/path/to/async-profiler -XX:+DTraceAllocProbes ProfileHeapAlloc

这里_ZN13SharedRuntime19dtrace_object_allocEP7oopDecimangled name对于 SharedRuntime::dtrace_object_alloc() 函数,只要 DTraceAllocProbes 标志打开,JVM 就会为每个堆分配调用该函数。

关于java - ThreadMXBean.getThreadAllocationBytes(long id) 中包含哪些 JVM 运行时数据区域,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59310446/

相关文章:

c - realloc 和 calloc 的动态内存分配问题

Java 相当于 MD5CryptoServiceProvider computeHash & ToBase64String

java - 方法 add(Element) 不适用于参数 (String[])

Java HashMap 像 ArrayList 一样加入了增强的 for 循环

android - 方法 XYZ 的代码超出了 65535 字节限制

java - 我如何读取 XX :MaxPermSize during runtime?

java - 检测从 bash 调用 java 程序何时失败

c - malloc(sizeof(struct xxxx)) 没有分配任何内存

java - 什么基于 JVM 的脚本语言支持@WebService 在运行时创建服务?

java - 在运行时设置 JVM 参数