zoukankan      html  css  js  c++  java
  • 对象的内存布局

    创建一个对象,不仅仅是找一块内存区域,然后把对象的值赋值进去那么简单。你想想,当我们 new 一个对象出来的时候,我们可以利用反射获取对象的一些信息。可以通过对象调用其中的一些方法。所以,在内存中,对象不仅仅包含了对象值那么简单,它还包含了更多的信息。

    这些信息保存在一个名为 对象头 的结构中。

    对象头

    一个对象实例,包含了 对象头、实例数据 和 对齐填充。

    对象头主要保存了这个实例的一些信息,实例数据保存了其中的实例数据。最后的 对齐填充,只是为了单纯填充空间。

    可以从图上看到,对象头主要分为两部分:

    • 运行时元数据,保存了对象运行时候的一些数据,如哈希值,GC 年龄,锁的状态等
    • 类型指针,保存了记录了类型元数据的指针。

    查看对象头

    为了查看对象头,我们得借用其他的 jar 包,我们在 maven 中写入:

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

    我们创建一个 Integer 对象:

    package heap;
    
    import org.openjdk.jol.info.ClassLayout;
    
    public class ObjectHeader {
        public static void main(String[] args) {
            Integer integer = new Integer(5);
            System.out.println(ClassLayout.parseInstance(integer).toPrintable());
        }
    }
    

    输出为:

    java.lang.Integer 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)                           be 22 00 f8 (10111110 00100010 00000000 11111000) (-134208834)
         12     4    int Integer.value                             5
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    

    从这里我们可以看出,的确一个 Integer 基本数据类型占用 4 个字节。但是整个对象,总共占用 16 个字节。

    前面的 12 个字节,就是对象头。

    实例数据

    实例数据,无非就是存储对象的内容。

    我们再来举个例子:

    package heap;
    
    import org.openjdk.jol.info.ClassLayout;
    
    import javax.print.DocFlavor;
    
    public class ObjectHeader {
        public static void main(String[] args) {
    
            String str_new = new String("Hello World");
            System.out.println(ClassLayout.parseInstance(str_new).toPrintable());
            System.out.println(str_new.hashCode());
            System.out.println(ClassLayout.parseInstance(str_new).toPrintable());
    
        }
    }
    

    最后的输出结果:

    java.lang.String 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)                           da 02 00 f8 (11011010 00000010 00000000 11111000) (-134216998)
         12     4   char[] String.value                              [H, e, l, l, o,  , W, o, r, l, d]
         16     4      int String.hash                               0
         20     4          (loss due to the next object alignment)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    
    -862545276
    java.lang.String 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)                           da 02 00 f8 (11011010 00000010 00000000 11111000) (-134216998)
         12     4   char[] String.value                              [H, e, l, l, o,  , W, o, r, l, d]
         16     4      int String.hash                               -862545276
         20     4          (loss due to the next object alignment)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    

    可以看到,在 string 实例中,还多了一项 String.hash 值,而且这个 hash 只有在你调用 实例的哈希值之后,才能被真正进行初始化赋值。

    对齐填充

    因为 hotspot 要求对象的起始地址必须为 8 字节的整数倍。所以,如果对象的内存布局不符合的话,就需要对齐进行补齐操作。

    讲完对象的内存布局,我们接下来讲解对象在堆中的创建以及垃圾回收过程。因为对象头中包含了 垃圾回收的相关信息,所以,我们可以边讲边对其进行分析。

  • 相关阅读:
    sqlalchemy 使用pymysql连接mysql 1366错误
    mysql之数据导出
    Go常见语句
    huffman code
    后缀数组,目前比较赶进度,而且有点难,所以放到以后再来看
    hash
    bipartite matching
    spanning tree
    拓扑排序
    Union Find
  • 原文地址:https://www.cnblogs.com/zhouzhiyao/p/13176839.html
Copyright © 2011-2022 走看看