美文网首页
java对象的内存布局

java对象的内存布局

作者: 定金喜 | 来源:发表于2020-06-15 23:56 被阅读0次

    1.java的内存布局

    对象在内存中的存储可以分为三个区域:对象头(Header),实例数据(Instance Data)和对齐填充(Padding)。


    对象头

    1. Mark Word:包含一系列的标记位,比如轻量级锁的标记位,偏向锁标记位等等。在32位系统占4字节,在64位系统中占8字节;
    • 32bit虚拟机中的Mark Word存储结构


    • 64bit虚拟机中的Mark Word存储结构


    1. 类型指针(Class Pointer)
    • 用来指向对象对应的Class对象(其对应的元数据对象)的内存地址。在32位系统占4字节,在64位系统中占8字节
    1. 数组长度
    • 如果对象是个数组的话,对象头中就必须要有一块用于记录数组长度的数据,不然虚拟机无法确定数组的大小。32位的JVM上,长度为32位;64位JVM则为64位。64位JVM如果开启+UseCompressedOops选项(开启指针压缩),该区域长度也将由64位压缩至32位。32位HotSpot VM是不支持UseCompressedOops参数的,只有64位HotSpot VM才支持。

    实际数据
    对象实际数据包括了对象的所有成员变量,其大小由各个成员变量的大小决定,比如:byte和boolean是1个字节,short和char是2个字节,int和float是4个字节,long和double是8个字节,reference是4个字节(64位系统中是8个字节)。

    Primitive Type Memory Required(bytes)
    boolean 1
    byte 1
    short 2
    char 2
    int 4
    float 4
    long 8
    double 8
    ref 4 或8

    对于reference类型来说,在32位系统上占用4bytes, 在64位系统上占用8bytes。

    对象填充
    Java对象占用空间是8字节对齐的,即所有Java对象占用bytes数必须是8的倍数。例如,一个包含两个属性的对象:int和byte,如果在32位系统中则占用需要4(mark word)+4(类型指针)+4(int)+1(byte)=13个字节,这时就需要加上大小为3字节的padding进行8字节对齐,最终占用大小为16个字节;如果在64位系统中未开启压缩指针(后续会介绍)的话占用8+8+4+1=21,加上大小为3字节的padding进行8字节对齐,最终占用大小为24个字节;如果在64位系统开启了压缩指针的话占用8+4+4+1=17,加上大小为7字节的padding进行8字节对齐,最终占用大小也为24个字节,但是这只是巧合,正好与未开启压缩指针相等。

    2.验证对象的内存布局

    添加maven依赖

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

    写一个测试程序

    package com.renlijia.multithread;
    
    import org.openjdk.jol.info.ClassLayout;
    /**
     * @Author: ding
     * @Date: 2020-06-15 23:12
     */
    public class Mem_Obj_内存布局 {
    
        public static void main(String[] args) {
    
            Object o = new Object();
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
    
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }
    }
    

    执行后结果:

    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           90 09 80 0d (10010000 00001001 10000000 00001101) (226494864)
          4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
          8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
         12     4        (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    对于锁的具体解释参照下文中的synchronized锁的介绍

    3.压缩指针理解

    安装jdk后在控制台执行:java -XX:+PrintCommandLineFlags -version可以打印出结果:
    -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
    java version "1.8.0_191"
    Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
    Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
    这里面有两个参数:UseCompressedClassPointers和UseCompressedOops是跟压缩指针相关,UseCompressedClassPointers开启后,对象布局里的类型指针会进行压缩,从8字节压缩到4个字节,这个参数只对类型指针进行压缩;UseCompressedOops是对普通的对象进行压缩,例如Object obj = new Object();会在内存中创建一个对象,并将指针指向它,这里面的指针是普通对象被引用的指针,开启后对此指针进行压缩,从8字节编程4个字节

    压缩指针失效

    1. 在32位系统中是没有压缩指针的,不存在失效与否,指针都是4个字节;
    2. 64位系统开启了压缩指针后,指针占用的存储从8字节变成4字节,但是当系统内存大于32G后,压缩指针失效,因为内存布局是8字节对齐,所以使用指针时,32G的数据实际只有4G的指针指向,因为每个对象占用的都是8的倍数的字节数(8字节对齐),所以使用4G就可以表示出32G的数据量,但是超过32G就无法表示,压缩指针失效,指针都占用8个字节,这就是为什么当内存从<32G变成大于32G后,应用占用的内存增大的原因;
    3. 堆内存小于4G时,不需要启用指针压缩,jvm会直接去除高32位地址,即使用低虚拟地址空间

    开启和取消压缩指针
    -XX:+UseCompressedOops 开启对象的压缩指针
    -XX:-UseCompressedOops 取消对象的压缩指针
    -XX:+UseCompressedClassPointers 开启类型指针的压缩
    -XX:-UseCompressedClassPointers 取消类型指针的压缩

    在小于32G的内存系统中压缩指针是默认开启的,当取消压缩指针,重新执行 Mem_Obj_内存布局 程序结果:
    -XX:-UseCompressedOops -XX:-UseCompressedClassPointers

    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
          4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
          8     4        (object header)                           00 2c c0 27 (00000000 00101100 11000000 00100111) (666905600)
         12     4        (object header)                           02 00 00 00 (00000010 00000000 00000000 00000000) (2)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    
    java.lang.Object object internals:
     OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
          0     4        (object header)                           90 09 63 03 (10010000 00001001 01100011 00000011) (56822160)
          4     4        (object header)                           00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
          8     4        (object header)                           00 2c c0 27 (00000000 00101100 11000000 00100111) (666905600)
         12     4        (object header)                           02 00 00 00 (00000010 00000000 00000000 00000000) (2)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    

    这个时候类型指针使用8个字节

    参考文章:
    https://www.jianshu.com/p/91e398d5d17c
    https://zhuanlan.zhihu.com/p/50984945
    https://blog.csdn.net/hongzb_2296/article/details/86505898
    https://blog.csdn.net/goodmentc/article/details/45880351
    https://blog.csdn.net/tongdanping/article/details/79647337

    相关文章

      网友评论

          本文标题:java对象的内存布局

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