Java 对象占用内存大小

作者: jijs | 来源:发表于2018-01-21 23:52 被阅读412次

    Java 对象

    如果想要了解java对象在内存中的大小,必须先要了解java对象的结构。

    HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)

    java 对象头

    • Mark Word
      HotSpot虚拟机的对象头(Object Header)包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希值(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂 不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。

    • Class Metadata Address
      存储该对象的 Class 对象的地址。就是该对象属于那个Class。

    • ArrayList
      存储数组的长度。
      如果是数组对象才会有此数据。非数组对象没有此数据。

    具体对象头占用的大小如下:

    长度 内容 说明
    32/64 bit Mark Word 存储对象的 hashCode 或锁信息等
    32/64 bit Class Metadata Address 存储到对象类型数据的指针
    32/64 bit ArrayList 数组的长度(如果当前对象是数组)

    从上面表格中,我们可以推断出:

    32位系统:

    • 对象头占用:32+32=64bit。 64bit/8=8byte。
    • 数组对象头占用:32+32+32=96bit。 96bit/8=12byte。

    64位系统:

    对象头占用:64+64=128bit。128bit/8=16byte。
    数组对象头占用:64+64+64=192bit。 192bit/8=24byte。

    实例数据

    实例数据就是,对象中的实例变量。
    实例变量类型分为:基本类型和引用类型。

    类型 32位系统占用空间 64位系统占用空间
    boolean 1 byte 1 byte
    byte 1 byte 1 byte
    char 2 byte 2 byte
    short 2 byte 2 byte
    int 4 byte 4 byte
    float 4 byte 4 byte
    long 8 byte 8 byte
    double 8 byte 8 byte
    ref 4 byte 8 byte

    对齐填充

    对象在堆中分配的最新存储单位是8byte。如果存储的数据不够8byte的倍数,则对齐填充够8的倍数个字节。

    Java 对象大小分析

    下面我们以 64 位的 JDK 进行分析 Java 对象在堆中的占用空间大小

    代码示例一

    public class StrObj1 {
        private String a;
    }
    public class StrObj2 {
        private String a;
        private String b;
    }
    public class StrObj3 {
        private String a;
        private String b;
        private String c;
    }
    public class StrObj4 {
        private String a;
        private String b;
        private String c;
        private String d;
    }
    public class NumObj {
        private int a;
        private int b;
        private int c;
        private int d;
    }
    public class Obj {
        public static void main(String[] args) {
            Obj obj = new Obj();
            StrObj1 s1 = new StrObj1();
            StrObj2 s2 = new StrObj2();
            StrObj3 s3 = new StrObj3();
            StrObj4 s4 = new StrObj4();
            Obj[] arrObj = new Obj[10];
            NumObj num = new NumObj();
                    //System.gc()  会出发 FullGC。
            System.gc();
        }
    }
    

    运行程序

    java -XX:+HeapDumpBeforeFullGC -XX:HeapDumpPath=D:\hprof\test2.hprof -XX:-UseCompressedOops cn.com.infcn.jmat.ObjectAnalyze

    启动参数说明:

    -XX:+UseCompressedOops
    开启指针压缩。(默认开启,该参数对64位虚拟机有用)
    -XX:-UseCompressedOops
    关闭指针压缩。
    其它参数具体 JVM 参数解释详见:生成 Heap Dump 的几种方式

    因为 System.gc(); 会出发FullGC,配合-XX:+HeapDumpBeforeFullGC 参数,会在 FullGC 前会在生成一个堆dump文件:D:\hprof\test2.hprof

    分析dump

    本案例,使用 jmat 工具进行分析 dump 文件。

    cn.com.infcn.jmat.Obj 对象分析

    从图中我们发现 cn.com.infcn.jmat.Obj 对象占用 16 byte 空间。
    非数组64位的对象头 占用16字节,而且改对象没有属性,16字节正好也是8的倍数,不需要填充,所以占用堆空间久违16字节。

    cn.com.infcn.jmat.StrObj1

    图中可以看出 cn.com.infcn.jmat.StrObj1 对象占用 24 byte 空间。

    对象头 16 byte
    1 个引用类型实例变量
    16 + 8 = 24 byte

    cn.com.infcn.jmat.StrObj2

    图中可以看出 cn.com.infcn.jmat.StrObj1 对象占用 32 byte 空间

    对象头 16 byte
    2 个引用类型实例变量
    16 + 2 * 8 = 32 byte

    cn.com.infcn.jmat.StrObj3

    图中可以看出 cn.com.infcn.jmat.StrObj1 对象占用 40 byte 空间

    对象头 16 byte
    3 个引用类型实例变量
    16 + 3 * 8 = 40 byte

    cn.com.infcn.jmat.StrObj4

    图中可以看出 cn.com.infcn.jmat.StrObj1 对象占用 48 byte 空间

    对象头 16 byte
    4个引用类型实例变量
    16 + 4 * 8 = 48 byte

    cn.com.infcn.jmat.NumObj

    图中可以看出 cn.com.infcn.jmat.NumObj 对象占用 32 byte 空间

    4个 int 类型实例变量
    16 + 4 * 4 = 32 byte

    cn.com.infcn.jmat.Obj[] 数组

    图中可以看出 cn.com.infcn.jmat.Obj[] 对象占用 104 byte 空间

    数组对象头 24 byte
    10 个 Obj 的引用。
    24 + 8 * 10 = 104 byte

    对象数组中存储的是对象的引用,而不是实际的数据。

    代码示例 二

    public class BooleanObj1 {
        boolean a;
    }
    
    ......
    
    public class BooleanObj8 {
        boolean a;
        boolean b;
        boolean c;
        boolean d;
        boolean e;
        boolean f;
        boolean g;
        boolean h;
    }
    public class BooleanObj9 {
        boolean a;
        boolean b;
        boolean c;
        boolean d;
        boolean e;
        boolean f;
        boolean g;
        boolean h;
        boolean i;
    }
    

    以指针非压缩方式执行,然后分析dump。


    从图中我们发现 BooleanObj1 和 BooleanObj8 大小一样都是24。
    而 BooleanObj9 的大小为32。

    BooleanObj1

    对象头 16 byte
    1 个 boolean 实例变量。

    16 + 1 = 17 byte。
    因为 17 byte 不是 8 的倍数,需要 对齐填充
    所以BooleanObj1 所占空间为 24 byte。

    BooleanObj8

    对象头 16 byte
    8 个 boolean 实例变量。

    16 + 8 = 24 byte。

    BooleanObj9

    对象头 16 byte
    9 个 boolean 实例变量

    16 + 9 =25 byte
    对齐填充 后为 32 byte

    想了解更多精彩内容请关注我的公众号

    相关文章

      网友评论

      • 0x70e8:问下啊,数组的最大长度不是Integer的最大值吗?不是4字节就可以了吗?为什么64位下对象头里面要使用8字节来存储数组长度啊
        0x70e8:@jijs 我去找了openjdk8的源码:
        在openjdk-8/hotspot/src/share/vm/oops/arrayOop.hpp里面的class arrayOopDesc
        找到size_t hs = align_size_up(length_offset_in_bytes() + sizeof(int), HeapWordSize); 但是还是不太确定是否确实是4字节,你8字节的结论是哪来的啊
        jijs:@0x70e8 这个还真没考虑过,应该是预留的吧,说不定哪天创建数组就支持 long 了。

      本文标题:Java 对象占用内存大小

      本文链接:https://www.haomeiwen.com/subject/olsqaxtx.html