Java中ObjectGraphMeasurer.measure结果转字节方法及术语咨询
嘿,我来帮你拆解这个Caliper的Footprint结果,一步步搞清楚这些指标和内存计算方式:
先搞懂Footprint里的关键指标
咱们先把每个字段的含义理清楚:
- Objects:你的对象图中包含的所有Java对象总数(比如各种POJO、集合、数组对象都算在内)。
- NonNullRefs:所有对象里指向其他非null对象的引用数量——简单说就是对象之间有效的关联,比如一个ArrayList里的每个非null元素,就是一个NonNullRef。
- NullRefs:对象中声明为引用类型但实际是null的字段数量——比如类里定义了
String username但没赋值,这个空字段就会被统计成一个NullRef。 - Primitives:对象图里所有基本类型的实例统计,后面的数字是对应类型的总数量(比如
int x 388091就是有388091个int类型的值)。
把Footprint转换为总字节数的计算方法
要算出实际内存占用,得结合JVM的内存模型(这里以64位JVM开启指针压缩为默认情况,这是现代JVM的主流配置):
步骤1:计算基本类型的总字节数
每种基本类型的字节大小是固定的,直接相乘求和就行:
- int:4字节 →
388091 × 4 = 1552364字节 - double:8字节 →
4375548 × 8 = 35004384字节 - long:8字节 →
2609976 × 8 = 20879808字节 - boolean:1字节(JVM中单个boolean字段占1字节,数组里的boolean也是1字节/元素)→
6398 × 1 = 6398字节 - float:4字节 →
76765 × 4 = 307060字节 - char:2字节 →
66658 × 2 = 133316字节
把这些加起来,基本类型总字节数是:1552364 + 35004384 + 20879808 + 6398 + 307060 + 133316 = 57883330 字节(约55.2MB)
步骤2:计算引用类型的总字节数
NonNullRefs + NullRefs 就是所有对象中引用类型字段的总数。在开启指针压缩的64位JVM中,每个引用字段占4字节;如果未开启指针压缩,每个引用占8字节。
以开启指针压缩为例:
总引用字段字节数 = (353968 + 1016731) × 4 = 1370699 × 4 = 5482796 字节(约5.23MB)
步骤3:计算所有对象的对象头总字节数
每个Java对象都有对象头,开启指针压缩时对象头是12字节,未开启是16字节。这里的对象数量就是Footprint里的Objects=270813:
开启指针压缩时:270813 × 12 = 3249756 字节(约3.1MB)
步骤4:加上对象对齐的开销
Java对象的大小必须是8字节的倍数,所以每个对象的实际大小会向上取整到最近的8字节。这部分开销很难精确计算(因为每个对象的字段数量不同),但通常可以估算为总对象大小的10%左右。
把前面三步的结果加起来,得到的近似值是:57883330 + 5482796 + 3249756 = 66615882 字节(约63.5MB),加上对齐开销后总内存大概在64-65MB之间。
更省心的方法:直接用Caliper的measureBytes
其实你不用手动计算这么麻烦,Caliper的ObjectGraphMeasurer提供了measureBytes(Object obj)方法,它会直接返回对象图的总内存占用字节数,已经自动考虑了对象头、对齐、指针压缩等所有细节,结果更准确。
示例代码:
long totalMemory = ObjectGraphMeasurer.measureBytes(yourTargetObject); System.out.println("Total memory footprint: " + totalMemory + " bytes");
内容的提问来源于stack exchange,提问作者sarah hamza

