Java对象在JVM中占用内存计算

本文探讨了JVM中对象的内存布局,通过Java Object Layout (JOL) 工具展示了不同压缩选项下对象的大小。当开启普通指针压缩(-XX:+UseCompressedOops)时,对象大小会减小,而开启类指针压缩(-XX:+UseCompressedClassPointers)会进一步减少空间损失。文章详细计算了各个场景下对象的头部信息、实例数据及对齐间隙,揭示了压缩选项如何影响64位HotSpot VM的对象内存占用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


关于对象的内容完整版在这里-Java的对象和JVM的对象

1 JOL

​ JOL(Java Object Layout),是openjdk的一个工具,可以查看详细的对象布局。

        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.16</version>
        </dependency>

​ 把包引入到项目中,放一段测试用的代码

import org.openjdk.jol.info.ClassLayout;

import java.util.List;

/**
 * @author 编程还未
 */
public class TestJOL {
    public static void main(String[] args) {
        User user = new User();
        System.out.println(ClassLayout.parseInstance(user).toPrintable());
    }

    static class User {
        byte a;
        short b;
        int c;
        long d;
        float e;
        double f;
        boolean g;
        char h;
        String i;
        Child child;
        List<Child> childList;
    }

    static class Child {
        String name;
    }
}

​ 开启指针压缩(-XX:+UseCompressedOops)运行结果如下:

com.program_highway.TestJOL$User object internals:
OFF  SZ                                TYPE DESCRIPTION               VALUE
  0   8                                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                                     (object header: class)    0x00c01200
 12   4                                 int User.c                    0
 16   8                                long User.d                    0
 24   8                              double User.f                    0.0
 32   4                               float User.e                    0.0
 36   2                               short User.b                    0
 38   2                                char User.h                     
 40   1                                byte User.a                    0
 41   1                             boolean User.g                    false
 42   2                                     (alignment/padding gap)   
 44   4                    java.lang.String User.i                    null
 48   4   com.program_highway.TestJOL.Child User.child                null
 52   4                      java.util.List User.childList            null
Instance size: 56 bytes
Space losses: 2 bytes internal + 0 bytes external = 2 bytes total

​ 关闭指针压缩(-XX:-UseCompressedOops)运行结果如下:

com.program_highway.TestJOL$User object internals:
OFF  SZ                                TYPE DESCRIPTION               VALUE
  0   8                                     (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                                     (object header: class)    0x00c01200
 12   4                                 int User.c                    0
 16   8                                long User.d                    0
 24   8                              double User.f                    0.0
 32   4                               float User.e                    0.0
 36   2                               short User.b                    0
 38   2                                char User.h                     
 40   1                                byte User.a                    0
 41   1                             boolean User.g                    false
 42   6                                     (alignment/padding gap)   
 48   8                    java.lang.String User.i                    null
 56   8   com.program_highway.TestJOL.Child User.child                null
 64   8                      java.util.List User.childList            null
Instance size: 72 bytes
Space losses: 6 bytes internal + 0 bytes external = 6 bytes total

2 对象大小计算

对象大小=对象头+实例数据

本小节内的运行结果,还是用JOL中的Java示例,Java1.8版本

alignment/padding gap:对齐/填充间隙(基本数据类型用的)。单个对象内部的成员变量填充对齐。开启指针压缩按4的倍数补齐;关闭指针压缩按8的倍数补齐。因为对象内部的引用类型要么4bytes要么8bytes,所以说这个属性是基本数据类型对齐用的,下面不再重复解释。

object alignment gap:对象对齐间隙。单个对象内部整体按8的倍数对齐。

-XX:+UseCompressedOops:普通指针压缩。64位HotSpot VM1.6 update14 开始正式支持。会把对象内的普通对象实例由8 bytes(64位)压缩到4 bytes(32位)。

-XX:+UseCompressedClassPointers:HotSpot会把对象头中_metadata指向klass的指针压缩为一个无符号32位整数,4bytes。

2.1 只开启普通指针压缩

JVM参数:-XX:+UseCompressedOops -XX:-UseCompressedClassPointers

com.program_highway.jol.TestJOL$User object internals:
OFF  SZ                                    TYPE DESCRIPTION               VALUE
  0   8                                         (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8                                         (object header: class)    0x00000266a13836e8
 16   8                                    long User.d                    0
 24   8                                  double User.f                    0.0
 32   4                                     int User.c                    0
 36   4                                   float User.e                    0.0
 40   2                                   short User.b                    0
 42   2                                    char User.h                     
 44   1                                    byte User.a                    0
 45   1                                 boolean User.g                    false
 46   2                                         (alignment/padding gap)   
 48   4                        java.lang.String User.i                    null
 52   4   com.program_highway.jol.TestJOL.Child User.child                null
 56   4                          java.util.List User.childList            null
 60   4                                         (object alignment gap)    
Instance size: 64 bytes
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total

该情况下计算:

对象大小=对象头+实例数据

对象大小=64 bytes

对象头=16 bytes,包含:object header: mark 8 bytes 和 object header: class 8 bytes

实例数据=分两大部分,一部分是基本数据类型,一部分是引用类型(对象)。基本数据类型的大小=8+8+4+4+2+2+1+1=30 bytes,补齐补2个bytes,32 bytes;引用类型的大小=4+4+4=12 bytes;

最终结果:对象大小(64 byets)=对象头(16bytes)+实例数据【基础类型(32bytes)+引用类型(12bytes)】+ 对象对齐间隙(4 bytes)

2.2 开启普通指针压缩和开启类指针压缩

JVM参数:-XX:+UseCompressedOops -XX:+UseCompressedClassPointers

注意:-XX:+UseCompressedClassPointers参数依赖-XX:+UseCompressedOops

这是JVM1.8默认的配置,两个压缩都开启。

com.program_highway.jol.TestJOL$User object internals:
OFF  SZ                                    TYPE DESCRIPTION               VALUE
  0   8                                         (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                                         (object header: class)    0xf800c143
 12   4                                     int User.c                    0
 16   8                                    long User.d                    0
 24   8                                  double User.f                    0.0
 32   4                                   float User.e                    0.0
 36   2                                   short User.b                    0
 38   2                                    char User.h                     
 40   1                                    byte User.a                    0
 41   1                                 boolean User.g                    false
 42   2                                         (alignment/padding gap)   
 44   4                        java.lang.String User.i                    null
 48   4   com.program_highway.jol.TestJOL.Child User.child                null
 52   4                          java.util.List User.childList            null
Instance size: 56 bytes
Space losses: 2 bytes internal + 0 bytes external = 2 bytes total

该情况下计算

对象大小=对象头+实例数据

对象大小=56 bytes

对象头=12 bytes,包含:object header: mark 8 bytes 和 object header: class 4 bytes

实例数据=分两大部分,一部分是基本数据类型,一部分是引用类型(对象)。基本数据类型的大小=8+8+4+4+2+2+1+1=30 bytes,补齐补2个bytes,32 bytes;引用类型的大小=4+4+4=12 bytes;

最终结果:对象大小(64 bytes)=对象头(12 bytes)+实例数据【基础类型(32 bytes)+引用类型(12 bytes)】

能被8整除,就不需要补齐了。

2.3 不开启压缩

JVM参数:-XX:-UseCompressedOops -XX:-UseCompressedClassPointers

com.program_highway.jol.TestJOL$User object internals:
OFF  SZ                                    TYPE DESCRIPTION               VALUE
  0   8                                         (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8                                         (object header: class)    0x000001c320aa36e8
 16   8                                    long User.d                    0
 24   8                                  double User.f                    0.0
 32   4                                     int User.c                    0
 36   4                                   float User.e                    0.0
 40   2                                   short User.b                    0
 42   2                                    char User.h                     
 44   1                                    byte User.a                    0
 45   1                                 boolean User.g                    false
 46   2                                         (alignment/padding gap)   
 48   8                        java.lang.String User.i                    null
 56   8   com.program_highway.jol.TestJOL.Child User.child                null
 64   8                          java.util.List User.childList            null
Instance size: 72 bytes
Space losses: 2 bytes internal + 0 bytes external = 2 bytes total

该情况下计算

对象大小=对象头+实例数据

对象大小=72 bytes

对象头=16 bytes,包含:object header: mark 8 bytes 和 object header: class 4 bytes

实例数据=分两大部分,一部分是基本数据类型,一部分是引用类型(对象)。基本数据类型的大小=8+8+4+4+2+2+1+1=30 bytes,8位补齐补2个bytes,32 bytes;引用类型的大小=8+8+8=24 bytes;

最终结果:对象大小(72 byets)=对象头(16 bytes)+实例数据【基础类型(32 bytes)+引用类型(24 bytes)】

能被8整除,就不需要补齐了。

2.4 计算总结

对象大小受三个JVM属性的影响,上述两个-XX:+/-UseCompressedOops -XX:+/-UseCompressedClassPointers,还有-XX:+/-CompactFields

-XX:+/-UseCompressedOops:开启/关闭普通指针压缩

-XX:+/-UseCompressedClassPointers:开启/关闭类指针压缩

-XX:+/-CompactFields:开启/关闭紧凑属性,

这三个属性默认都是开启的,在默认情况下计算对象大小。

对象大小=对象头+实例数据+object alignment gap(对象对齐间隙)

对象头=12 bytes

实例数据=基础数据类型占用之和+alignment/padding gap(对齐/填充缝隙)+引用类型占用之和(一个对象占4 bytes)+object alignment gap(对象对齐间隙)

2.5 加餐

有继承关系如何处理?(默认三个属性都开启)

如果上述类User有继承关系且父类有属性,User类会有父类的属性,父类的基本数据类型会单独计算。

Father类,User类继承Father

    static class Father {
        private String address;
        short age;
    }

结果:

com.program_highway.jol.TestJOL$User object internals:
OFF  SZ                                    TYPE DESCRIPTION               VALUE
  0   8                                         (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                                         (object header: class)    0xf800c182
 12   2                                   short Father.age                0
 14   2                                         (alignment/padding gap)   
 16   4                        java.lang.String Father.address            null
 20   4                                     int User.c                    0
 24   8                                    long User.d                    0
 32   8                                  double User.f                    0.0
 40   4                                   float User.e                    0.0
 44   2                                   short User.b                    0
 46   2                                    char User.h                     
 48   1                                    byte User.a                    0
 49   1                                 boolean User.g                    false
 50   2                                         (alignment/padding gap)   
 52   4                        java.lang.String User.i                    null
 56   4   com.program_highway.jol.TestJOL.Child User.child                null
 60   4                          java.util.List User.childList            null
Instance size: 64 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

有继承关系的对象大小=对象头+父基本数据类型占用大小+父基本数据类型的alignment/padding gap(对齐/填充缝隙)+ 自己的基本数据类型占用大小+自己的基本数据类型的alignment/padding gap(对齐/填充缝隙)+ 父类和自己的引用类型占用之和(一个对象占4 bytes)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程还未

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值