You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java对象内存大小检测方法对比:结果差异及选型疑问

Java对象内存大小检测:两种方法的差异及正确选择

嘿,我来帮你理清这两种方法到底在测什么,以及为什么结果差这么大——其实它们测的根本不是一回事儿!

先拆解两种方法的本质

1. Runtime内存差值法(Jon Skeet的方法)

这个方法是通过计算创建对象前后的堆内存使用差值来估算,但它有几个关键的局限性:

  • GC的不确定性:哪怕你调用了System.gc(),JVM也只是“建议”执行垃圾回收,不能保证立刻清干净。所以前后的内存差值可能混杂着其他未回收的对象,或者包含了JVM为了分配内存提前申请的整块内存(不是刚好对象需要的大小)。
  • 测的是JVM实际分配的堆内存,不是对象本身的精确大小:JVM里每个对象都有对象头(Mark Word、类指针)、内存对齐的额外开销,数组还有单独的长度字段。比如你测的LotsOfBooleans对象,实际在堆里占的内存远不止1字节的boolean字段,还要加上对象头和对齐填充,而Runtime方法算的是JVM给这些对象+数组分配的整块内存的变化,不是单个对象的精确内存占用。
  • Jon Skeet的那个回答其实是用来大致监控堆内存的变化趋势,不是精确测量单个对象的内存占用哦。

2. ByteArrayOutputStream序列化法

这个方法测的是对象序列化后的字节大小,和堆内存里的实际占用完全是两码事:

  • 序列化只会保存对象的字段数据和序列化所需的元信息(比如类的标识),会忽略JVM对象头、内存对齐、类元数据引用这些堆内存里的额外开销。
  • 对于数组来说,序列化只记录数组长度和每个元素的字段数据,但堆里的数组本身有对象头,每个对象元素也有自己的对象头,这些都不会被序列化进去。
    所以这个值只能用来判断对象在网络传输、存储时的大小,和它在JVM里占的内存没关系。

要精确测对象内存大小?用这些方法

如果你想知道对象在JVM堆里的实际内存占用,推荐这两种靠谱的方式:

方法一:用JOL(Java Object Layout)工具

这是OpenJDK官方推出的工具,能直接展示对象的内存布局和精确大小,用起来超方便,不需要写agent。比如给你的测试类加一段代码:

import org.openjdk.jol.info.ClassLayout;

public class Test {
    // 你的原有代码...
    
    public static void main(String[] args) throws Exception {
        // 原有代码...
        
        // 用JOL查看对象内存布局
        System.out.println("LotsOfBooleans对象内存布局:");
        System.out.println(ClassLayout.parseClass(LotsOfBooleans.class).toPrintable());
        System.out.println("\nLotsOfInts对象内存布局:");
        System.out.println(ClassLayout.parseClass(LotsOfInts.class).toPrintable());
    }
}

它会输出详细的内存结构,包括对象头的大小、字段偏移、对齐字节,还有总大小,一目了然。

方法二:用JDK的Instrumentation API

这是最底层最准确的方法,能获取对象的浅内存大小(不包括引用对象的内存)。你需要写一个简单的Java Agent:

import java.lang.instrument.Instrumentation;

public class ObjectSizeAgent {
    private static Instrumentation inst;

    public static void premain(String agentArgs, Instrumentation instrumentation) {
        inst = instrumentation;
    }

    public static long getObjectSize(Object obj) {
        return inst.getObjectSize(obj);
    }
}

然后打包成jar,在MANIFEST.MF里指定Premain-Class: ObjectSizeAgent,运行时用-javaagent:你的agent.jar参数加载,之后就能调用ObjectSizeAgent.getObjectSize()获取精确的对象大小了。

方法三:手动计算(适合理解内存模型)

如果只是想学习,也可以根据JVM的内存规则手动算:

  • 64位JVM默认对象头是16字节(开启指针压缩的话是12字节);
  • 实例字段按类型占对应字节(boolean1、int4等),还要对齐到8字节的倍数;
  • 数组对象额外加4字节存长度,再加上元素内存和对齐。
    比如开启指针压缩时,LotsOfBooleans对象是12字节头+1字节boolean,对齐到16字节,100万个的话就是16*1000000=16000000字节,和你Runtime方法得到的15914240接近,因为还要减去数组本身的内存开销。

最后总结一下

  • Runtime差值法:适合看堆内存整体变化趋势,别用来精确测对象大小;
  • 序列化法:测的是对象序列化后的字节数,和堆内存无关;
  • 要精确测JVM堆里的对象大小,优先用JOL工具,简单直观;追求底层准确就用Instrumentation API。

内容的提问来源于stack exchange,提问作者Elye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 08:47:38