我试图获取某些代码段的内存消耗。经过一番搜索,我意识到可以使用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);
我在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所指示的,它返回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加载引用的类-java.lang.Long
。
ClassLoader.loadClass
用Java实现,它当然可以分配Java对象。例如,ClassLoader.loadClass
方法在getClassLoadingLock
中创建一个新的锁定对象和一个新条目:
getClassLoadingLock
此外,当在系统字典中进行类名查找时,JVM会创建一个新的String对象。
我使用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所做的所有堆分配。分配了13个Java对象。这是可点击的交互式火焰图:
该图由以下测试程序生成:
java.lang.Long
如何运行:
这里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标志打开时,JVM会为每个堆分配调用它。