对象的内存布局

HotSpot VM 中,对象在堆内存中的存储布局分为:

  • 对象头(Header)

  • 实例数据(Instance Data)

  • 对齐填充(Padding)


1、对象头

  • Mark Word,对象运行时数据,动态定义位数据。包含 HashCode、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等。32 位和 64 位的 VM 里 Mark Word 的长度分别 32 bit 和 64 bit。
  • 类型指针,对象指向所属 class 的元数据的指针,32 位 VM 4 byte,64 位 VM 8 byte(-XX:+UseCompressedOops 压缩指针 4 byte)。非必需,取决 JVM 实现。
  • 数组对象必须包含长度信息,4 byte。


2、实例数据

  • 按分配策略和源码定义的顺序,存储对象的字段数据。
  • 8 byte:long、double
  • 4 byte:int、float、oops(Ordinary Object Pointers)
  • 2 byte:short、char
  • 1 byte:byte
  • 1 bit:boolean


3、对齐填充

  • 非必需,保证对象大小是 8 字节的整数倍进行填充的长度。
  • 没有特殊含义,仅占位。


对象的创建

new 语法创建普通 Java 对象的过程

  • 检查类是否已加载、解析、初始化,若没有则进行。
  • 为新对象分配内存。规整的内存使用指针碰撞(Bump The Pointer);不规整的内存使用列表记录可用内存的方式配分叫空闲列表(Free List)。
  • 配分内存的两种安全方式:CAS;每个线程在 Heap 中预先分配一小块内存,即本地线程分配缓存(Thread Local Allocation Buffer)。
  • 初始化分配的内存,字段设为默认零值
  • 根据 JVM 运行的情况,设置对象头信息。如类的元数据、HashCode、GC 分代年龄、偏向锁...
  • 执行构造函数、字段赋值...


对象的访问定位

通过栈上的 reference 数据定位、访问堆上的对象,两种方式:

  • 句柄访问。栈中本地变量表里的 reference 存储的是堆中句柄池中对象的句柄地址,句柄包含了对象实例数据和类型数据的地址。
  • 直接指针。栈中本地变量表里的 reference 存储的是对象数据地址,可以直接访问,少一次寻址访问速度快。



分析一把

那么下面这个字符串对象占用多大内存?

new String("ConstXiong");


  • 假设是 JVM 是 64 位的 HotSpot VM,JDK 8,单纯看这个 String 对象的内存大小,不看常量池,有类型指针,默认开启指针压缩。
  • String 类的静态变量存储在类的 Class 对象中,不计算;父类和接口都不包含普通对象字段,所以只需关注下面源码。

JDK 8 String 源码

public final class String
    implements java.io.SerializableComparable<String>, CharSequence 
{
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
}


  • Mark Word 64 bit = 8 byte

  • 类型指针 4 byte

  • 实例数据 value 变量,4 byte

  • 实例数据 hash,是 int,4 byte

  • 对齐 4 byte

共 24 字节。使用 openjdk-jol 工具打印内存布局如下:

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)                           d0 16 40 17 (11010000 00010110 01000000 00010111) (390076112)
     12     4   char[] String.value                              [C, o, n, s, t, X, i, o, n, g]
     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


补充一下,实例数据 value 变量对应的 char[] 对象 = ['C','o','n','s','t','X','i','o','n','g']),数组对象比普通对象对象头多了一个 int 记录数组长度,64 bit(Mark Word) + 4 byte(类型指针) + 4 byte(数组长度) + 10 * 2 byte(10个字符) + 0 byte(补齐) = 40 byte

[C 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)                           08 02 14 17 (00001000 00000010 00010100 00010111) (387187208)
     12     4        (object header)                           000 00 00 (00001010 00000000 00000000 00000000) (10)
     16    20   char [C.<elements>                             N/A
     36     4        (loss due to the next object alignment)
Instance size: 40 bytes


code:

package constxiong;

import org.openjdk.jol.info.ClassLayout;

public class TestObjectSize {

    public static void main(String[] args{
        System.out.println(ClassLayout.parseInstance(new String("ConstXiong")).toPrintable());
        System.out.println(ClassLayout.parseInstance(new char[]{'C''o''n''s''t''X''i''o''n''g'}).toPrintable());
    }
}


maven pom:

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


更多相关文章

  1. 从对象生命周期的经验统计到垃圾回收算法
  2. 什么样的 Java 对象会被当垃圾回收?
  3. 【MySQL】数据库版本升级:mysql 5.6 升级到 mysql 5.7
  4. 帆软报表自定义函数-取json数据

随机推荐

  1. 将jQuery datepicker应用到多个实例
  2. 常见的面试题
  3. 是什么导致Meteor中的“模板未定义”?
  4. adobeindesign JavaScript XML:如何以编
  5. 从文本框值生成条形码图像
  6. 谷歌地图在角度指令中不起作用
  7. js金额数字格式化实现代码(三位加逗号处
  8. 另一个iframe中的iframe的onload函数
  9. JavaScript 中的函数介绍
  10. JavaScript实现数学里的排列组合的A和C运