JVM-3:Java对象存储

一个Java对象可以分为三部分存储在内存中,分别是:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。

1. 对象头

在HotSpot虚拟机中,对象头可以分为两部分。

1.1 对象自身的运行时数据

这部分存储包括哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。这部分数据被官方称为Mark Word,在32位和64位的虚拟机中的大小分别为32bit和64bit。

由于对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以提高存储空间的利用率。即这部分数据会根据对象的状态来分配存储空间。以下是一个64位虚拟机中的例子(引用链接):

|----------------------------------------------------------------------------------------|--------------------|
|                                    Object Header (64 bits)                             |        State       |
|-------------------------------------------------------|--------------------------------|--------------------|
|                  Mark Word (32 bits)                  |      Klass Word (32 bits)      |                    |
|-------------------------------------------------------|--------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |      OOP to metadata object    |       Normal       |
|-------------------------------------------------------|--------------------------------|--------------------|
|  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |      OOP to metadata object    |       Biased       |
|-------------------------------------------------------|--------------------------------|--------------------|
|               ptr_to_lock_record:30          | lock:2 |      OOP to metadata object    | Lightweight Locked |
|-------------------------------------------------------|--------------------------------|--------------------|
|               ptr_to_heavyweight_monitor:30  | lock:2 |      OOP to metadata object    | Heavyweight Locked |
|-------------------------------------------------------|--------------------------------|--------------------|
|                                              | lock:2 |      OOP to metadata object    |    Marked for GC   |
|-------------------------------------------------------|--------------------------------|--------------------|

1.2 对象的类型指针

即指向对象的类元数据的指针。虚拟机可以通过该指针判定对象实例属于哪个类。

在Java对象中比较特殊的是Java数组,一个数组实例的对象头中必须记录数组的长度。JVM可以通过对象头中的数组长度数据来判定数组的大小,这是访问数组类型的元数据无法得到的。

2. 对象的实例数据

前面提到对象头是对象的额外开销,只有实例数据才是一个对象实例存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。这部分内容同时记录了子类从父类继承所得的各类型数据。

3. 填充

对齐填充在对象数据中并不是必然的,只是起着占位符的作用,没有特别含义。HotSpot要求对象起始地址必须是8字节的整数倍。对象头的大小刚好符合要求,因此当实例数据没有对齐时,就需要通过填充来对齐数据。

4. 获取类的元数据

虚拟机在加载类的时候会将类的信息、常量、静态变量和即时编译器编译后的代码等数据存储在方法区(Method Area)。类的元数据,即类的数据描述,也被存在方法区。我们知道对象头中会存有对象的类型指针,通过类型指针可以获取类的元数据。因此,对象的类型指针其实指向的是方法区的某个存有类信息的地址。

但是,并不是每个对象实例都存有对象的类型指针。根据对象访问定位方法的不同,对象的类型指针被存放在不同的区域。

  • 通过句柄访问对象
    • 对象的类型指针被存放在句柄池中;
  • 通过Reference指针直接访问对象
    • 对象的类型指针被存放在对象本身的数据中。

5. 总结

因此,Java的对象数据存储可以理解为:

  • 引用类型(指向对象的Reference)
    • 存储在栈中
  • 对象的类的元数据 (Class MetaData)
    • 存储在方法区中
  • 对象的实例数据
    • 存储在堆中

6. 参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,977评论 18 399
  • 早晨起床效率很高,做了早餐,送宝贝去学校,回来发发面,自己梳妆打扮,居然到单位时间刚刚好。 看看大盘比期望的还要红...
    亲子_87c2阅读 754评论 0 0
  • 难怪女儿这么喜欢绿山墙的安妮,当我在看《汤姆叔叔的小屋时》,她极力推荐我看看《绿山墙的安妮》,这些都是我给她买的她...
    两个阿丫丫阅读 4,481评论 0 1
  • 人一生估计还是苦多吧,常言道:"人生不如意事十之八九,能与人言无一二"。并且大多数苦还是说不出来的,只能自己慢慢体...
    goldfish2017阅读 1,040评论 0 0