ThreadMXBean.getThreadAllocatedBytes(long id)中包含了哪些JVM运行时数据区
What JVM runtime data area are included in ThreadMXBean.getThreadAllocatedBytes(long id)
我试图获取一些代码片段的内存消耗。经过一番搜索,我意识到 ThreadMXBean.getThreadAllocatedBytes(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);
I 运行 此代码在 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 之间的巨大差异感到困惑。
实际上,我猜这是由 class 加载引起的。所以我取消了第3行代码的注释,结果如下:
0 Long: 48 bytes
1 Long: 72 bytes
10 Long: 288 bytes
20 Long: 528 bytes
30 Long: 768 bytes
看来结果证实了我的猜测。但是,在我看来,class结构体的信息是存放在方法区的,不属于堆内存的一部分。正如 ThreadMXBean.getThreadAllocatedBytes(long id) 的 Javadoc 所示,它 returns the total amount of memory allocated in heap memory
。我错过了什么吗?
测试的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 加载引用的 class - java.lang.Long
.
ClassLoader.loadClass
is implemented in Java, and it can certainly allocate Java objects. For instance, 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;
}
此外,在系统字典中进行 class 名称查找时,JVM 会创建一个新的 String 对象。
我使用 async-profiler 记录 JVM 在加载 java.lang.Long
class 时所做的所有堆分配。这是可点击的交互式火焰图:
该图包括 13 个样本 - 每个分配的对象一个。已分配对象的类型未显示,但可以从上下文(堆栈跟踪)中轻松猜出。
- 绿色表示 Java 堆栈跟踪;
- 黄色表示 VM 堆栈跟踪。
请注意,每个 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_allocEP7oopDesci
是 mangled name for SharedRuntime::dtrace_object_alloc()
函数,只要 DTraceAllocProbes
标志打开,JVM 就会为每个堆分配调用它。
我试图获取一些代码片段的内存消耗。经过一番搜索,我意识到 ThreadMXBean.getThreadAllocatedBytes(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);
I 运行 此代码在 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 之间的巨大差异感到困惑。 实际上,我猜这是由 class 加载引起的。所以我取消了第3行代码的注释,结果如下:
0 Long: 48 bytes
1 Long: 72 bytes
10 Long: 288 bytes
20 Long: 528 bytes
30 Long: 768 bytes
看来结果证实了我的猜测。但是,在我看来,class结构体的信息是存放在方法区的,不属于堆内存的一部分。正如 ThreadMXBean.getThreadAllocatedBytes(long id) 的 Javadoc 所示,它 returns the total amount of memory allocated in heap memory
。我错过了什么吗?
测试的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 加载引用的 class - java.lang.Long
.
ClassLoader.loadClass
is implemented in Java, and it can certainly allocate Java objects. For instance, 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;
}
此外,在系统字典中进行 class 名称查找时,JVM 会创建一个新的 String 对象。
我使用 async-profiler 记录 JVM 在加载 java.lang.Long
class 时所做的所有堆分配。这是可点击的交互式火焰图:
该图包括 13 个样本 - 每个分配的对象一个。已分配对象的类型未显示,但可以从上下文(堆栈跟踪)中轻松猜出。
- 绿色表示 Java 堆栈跟踪;
- 黄色表示 VM 堆栈跟踪。
请注意,每个 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_allocEP7oopDesci
是 mangled name for SharedRuntime::dtrace_object_alloc()
函数,只要 DTraceAllocProbes
标志打开,JVM 就会为每个堆分配调用它。