• Java对象头详解


    由于Java面向对象的思想,在JVM中需要大量存储对象,存储时为了实现一些额外的功能,需要在对象中添加一些标记字段用于增强对象功能,这些标记字段组成了对象头。

    1.对象头形式

    JVM中对象头的方式有以下两种(以32位JVM为例):

    1.1.普通对象

    |--------------------------------------------------------------|
    |                     Object Header (64 bits)                  |
    |------------------------------------|-------------------------|
    |        Mark Word (32 bits)         |    Klass Word (32 bits) |
    |------------------------------------|-------------------------|
    

    1.2.数组对象

    |---------------------------------------------------------------------------------|
    |                                 Object Header (96 bits)                         |
    |--------------------------------|-----------------------|------------------------|
    |        Mark Word(32bits)       |    Klass Word(32bits) |  array length(32bits)  |
    |--------------------------------|-----------------------|------------------------|
    

    2.对象头的组成

    2.1.Mark Word

    这部分主要用来存储对象自身的运行时数据,如hashcode、gc分代年龄等。mark word的位长度为JVM的一个Word大小,也就是说32位JVM的Mark word为32位,64位JVM为64位。
    为了让一个字大小存储更多的信息,JVM将字的最低两个位设置为标记位,不同标记位下的Mark Word示意如下:

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

    其中各部分的含义如下:
    lock:2位的锁状态标记位,由于希望用尽可能少的二进制位表示尽可能多的信息,所以设置了lock标记。该标记的值不同,整个mark word表示的含义不同。

    biased_locklock状态
    0 01 无锁
    1 01 偏向锁
    0 00 轻量级锁
    0 10 重量级锁
    0 11 GC标记

    biased_lock:对象是否启用偏向锁标记,只占1个二进制位。为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。
    age:4位的Java对象年龄。在GC中,如果对象在Survivor区复制一次,年龄增加1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,所以最大值为15,这就是-XX:MaxTenuringThreshold选项最大值为15的原因。
    identity_hashcode:25位的对象标识Hash码,采用延迟加载技术。调用方法System.identityHashCode()计算,并会将结果写到该对象头中。当对象被锁定时,该值会移动到管程Monitor中。
    thread:持有偏向锁的线程ID。
    epoch:偏向时间戳。
    ptr_to_lock_record:指向栈中锁记录的指针。
    ptr_to_heavyweight_monitor:指向管程Monitor的指针。

    64位下的标记字与32位的相似,不再赘述:

    |------------------------------------------------------------------------------|--------------------|
    |                                  Mark Word (64 bits)                         |       State        |
    |------------------------------------------------------------------------------|--------------------|
    | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |       Normal       |
    |------------------------------------------------------------------------------|--------------------|
    | thread:54 |       epoch:2        | unused:1 | age:4 | biased_lock:1 | lock:2 |       Biased       |
    |------------------------------------------------------------------------------|--------------------|
    |                       ptr_to_lock_record:62                         | lock:2 | Lightweight Locked |
    |------------------------------------------------------------------------------|--------------------|
    |                     ptr_to_heavyweight_monitor:62                   | lock:2 | Heavyweight Locked |
    |------------------------------------------------------------------------------|--------------------|
    |                                                                     | lock:2 |    Marked for GC   |
    |------------------------------------------------------------------------------|--------------------|
    

    2.2.class pointer

    这一部分用于存储对象的类型指针,该指针指向它的类元数据,JVM通过这个指针确定对象是哪个类的实例。该指针的位长度为JVM的一个字大小,即32位的JVM为32位,64位的JVM为64位。
    如果应用的对象过多,使用64位的指针将浪费大量内存,统计而言,64位的JVM将会比32位的JVM多耗费50%的内存。为了节约内存可以使用选项+UseCompressedOops开启指针压缩,其中,oop即ordinary object pointer普通对象指针。开启该选项后,下列指针将压缩至32位:

    1. 每个Class的属性指针(即静态变量)
    2. 每个对象的属性指针(即对象变量)
    3. 普通对象数组的每个元素指针

    当然,也不是所有的指针都会压缩,一些特殊类型的指针JVM不会优化,比如指向PermGen的Class对象指针(JDK8中指向元空间的Class对象指针)、本地变量、堆栈元素、入参、返回值和NULL指针等。

    2.3.array length

    如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,这部分数据的长度也随着JVM架构的不同而不同:32位的JVM上,长度为32位;64位JVM则为64位。64位JVM如果开启+UseCompressedOops选项,该区域长度也将由64位压缩至32位

    附:对象头相关的资料

    markOop.hpp
    CompressedOops
    JVM优化之压缩普通对象指针
    What is in java object header

     
     
    23人点赞
     
     
    "小礼物走一走,来简书关注我"
    还没有人赞赏,支持一下
    Hypercube 不想当架构师的程序员不是好的产品经理 阅文集团码农
    总资产213 (约15.87元)共写了6.9W字获得436个赞共908个粉丝
     
     
    全部评论7只看作者
    按时间倒序
    按时间正序
     
    5楼 03.02 22:53
    jdk8

    UseCompressedOops 和 UseCompressedClassPointers 应该是默认开启的。
    UseCompressedOops 关闭的情况下,UseCompressedClassPointers 就算强制开启也无效,这个时候object指针和 对象类型指针都是8bytes。
    UseCompressedOops 开启的情况下,UseCompressedClassPointers 是可以关闭的,这个时候object指针是4bytes大小,对象类型指针大小是8bytes(在UseCompressedClassPointers关闭的情况下)
    回复
     
     
    4楼 2019.09.09 17:38
    ”64位的JVM将会比32位的JVM多耗费50%的内存“这句话多耗费50%是怎么计算的?
    回复
     
    Hypercube作者
    2019.09.11 21:36
    实际测试出来的,可以创建100万个integer对象算算或者直接测试
    回复
     
    添加新评论
     
    3楼 2019.09.09 17:20
    麻烦告诉下这个内容是从什么书上摘下来的呢?
    回复
     

    Hypercube作者
    2019.09.11 21:33
    最后附上的参考资料
    回复
     
    添加新评论
     
    2楼 2019.07.29 20:50
    不太理解这个epoch 偏向时间戳是个干嘛的
    回复
     

    Hypercube作者
    2019.07.31 14:23
    https://juejin.im/post/5c17964df265da6157056588 可以看看这篇文章,详细的解释我再去看看代码
    回复
     
    添加新评论
     

     



    作者:Hypercube
    链接:https://www.jianshu.com/p/3d38cba67f8b
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    经典SQL语句大全 学者必看
    13个SQL优化技巧
    全面解析SQL SERVER 的左右内连接
    ORM框架
    JPA SQL 的复杂查询createNamedQuery
    SQL 复杂查询
    前端学习(十三)js运算符(笔记)
    前端学习(十二)js数据类型(笔记)
    前端学习(十一)函数(笔记)
    前端学习(十)初识js(笔记)
  • 原文地址:https://www.cnblogs.com/makai/p/12466541.html
Copyright © 2020-2023  润新知