Java对象结构

概述

对象实例由对象头、实例数据组成,其中对象头包括markword和类型指针,如果是数组,还包括数组长度;

| 类型 | 32位JVM | 64位JVM|
| ------ ---- | ------------| --------- |
| markword | 32bit | 64bit |
| 类型指针 | 32bit |64bit ,开启指针压缩时为32bit |
| 数组长度 | 32bit |32bit |

header.png
compressed_header.png

可以看到

  • 开启指针压缩时,markword占用8bytes,类型指针占用8bytes,共占用16bytes;
  • 未开启指针压缩时,markword占用8bytes,类型指针占用4bytes,但由于java内存地址按照8bytes对齐,长度必须是8的倍数,因此会从12bytes补全到16bytes;
  • 数组长度为4bytes,同样会进行对齐,补足到8bytes;

另外从上面的截图可以看到,开启指针压缩之后,对象类型指针为0xf800c005,但实际的类型指针为0x7c0060028;那么指针是如何压缩的呢?实际上由于java地址一定是8的倍数,因此将0xf800c005*8即可得到实际的指针0x7c0060028,关于指针压缩的更多知识可参考官方文档

markword结构

markword的结构,定义在markOop.hpp文件:

  32 bits:
  --------
  hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
  JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
  size:32 ------------------------------------------>| (CMS free block)
  PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)

  64 bits:
  --------
  unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
  JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
  size:64 ----------------------------------------------------->| (CMS free block)

  unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
  JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
  narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
  unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
 [ptr             | 00]  locked             ptr points to real header on stack
 [header      | 0 | 01]  unlocked           regular object header
 [ptr             | 10]  monitor            inflated lock (header is wapped out)
 [ptr             | 11]  marked             used by markSweep to mark an object
                                              

由于目前基本都在使用64位JVM,此处不再对32位的结构进行详细说明:

偏向锁标识位 锁标识位 锁状态 存储内容
0 01 未锁定 hash code(31),年龄(4)
1 01 偏向锁 线程ID(54),时间戳(2),年龄(4)
00 轻量级锁 栈中锁记录的指针(64)
10 重量级锁 monitor的指针(64)
11 GC标记 空,不需要记录信息

此处,有几点要注意:

  • 如果对象没有重写hashcode方法,那么默认是调用os::random产生hashcode,可以通过System.identityHashCode获取;os::random产生hashcode的规则为:next_rand = (16807seed) mod (2*31-1),因此可以使用31位存储;另外一旦生成了hashcode,JVM会将其记录在markword中;
  • GC年龄采用4位bit存储,最大为15,例如MaxTenuringThreshold参数默认值就是15;
  • 当处于轻量级锁、重量级锁时,记录的对象指针,根据JVM的说明,此时认为指针仍然是64位,最低两位假定为0;当处于偏向锁时,记录的为获得偏向锁的线程指针,该指针也是64位;
 We assume that stack/thread pointers have the lowest two bits cleared.
ObjectMonitor* monitor() const {
    assert(has_monitor(), "check");
    // Use xor instead of &~ to provide one extra tag-bit check.
    return (ObjectMonitor*) (value() ^ monitor_value);//monitor_value=2,value最右两位为10,因此异或之后最右两位为0
  }
JavaThread* biased_locker() const {
    assert(has_bias_pattern(), "should not call this otherwise");
    return (JavaThread*) ((intptr_t) (mask_bits(value(), ~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place))));
//~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place)为11111111111111111111110010000000,计算后的结果中,低10位全部为0;
  }

由于java中内存地址都是8的倍数,因此可以理解为最低3bit为0,因此假设轻量级和重量级锁的最低2位为0是成立的;但为什么偏向锁的最低10位都是0?查看markOop.hpp文件,发现有这么一句话:

// Alignment of JavaThread pointers encoded in object header required by biased locking
  enum { biased_lock_alignment    = 2 << (epoch_shift + epoch_bits)
//epoch_shift+epoch_bits=10
  };

thread.hpp中重载了operator new:

void* operator new(size_t size) { return allocate(size, true); }

// ======= Thread ========
// Support for forcing alignment of thread objects for biased locking
void* Thread::allocate(size_t size, bool throw_excpt, MEMFLAGS flags) {
  if (UseBiasedLocking) {
    const int alignment = markOopDesc::biased_lock_alignment;//10
    size_t aligned_size = size + (alignment - sizeof(intptr_t));
    void* real_malloc_addr = throw_excpt? AllocateHeap(aligned_size, flags, CURRENT_PC)
                                          : os::malloc(aligned_size, flags, CURRENT_PC);
    void* aligned_addr     = (void*) align_size_up((intptr_t) real_malloc_addr, alignment);
    assert(((uintptr_t) aligned_addr + (uintptr_t) size) <=
           ((uintptr_t) real_malloc_addr + (uintptr_t) aligned_size),
           "JavaThread alignment code overflowed allocated storage");
    if (TraceBiasedLocking) {
      if (aligned_addr != real_malloc_addr)
        tty->print_cr("Aligned thread " INTPTR_FORMAT " to " INTPTR_FORMAT,
                      real_malloc_addr, aligned_addr);
    }
    ((Thread*) aligned_addr)->_real_malloc_address = real_malloc_addr;
    return aligned_addr;
  } else {
    return throw_excpt? AllocateHeap(size, flags, CURRENT_PC)
                       : os::malloc(size, flags, CURRENT_PC);
  }
}

如果开启了偏移锁,在创建线程时,线程地址会进行对齐处理,保证低10位为0

实例数据

实例数据中主要包括对象的各种成员变量,包括基本类型和引用类型;static类型的变量会放到java/lang/Class中,而不会放到实例数据中;
对于引用类型的成员(包括string),存储的指针;对于基本类型,直接存储内容;通常会将基本类型存储在一起,引用类型存储在一起;
例如类Test的成员定义如下:

 private static Test t1=new Test();
 private Test t2;
 private int a=5;
 private Integer b=7;
 private String c="112";
 private BigDecimal d=new BigDecimal("5");
 private long e=9l;
body.png

可以看到long e、int a为基本类型,存储在一起;其它的引用类型存储在一起;int占用4bytes,不足8bytes,自动补足到8bytes;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,165评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,503评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,295评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,589评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,439评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,342评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,749评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,397评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,700评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,740评论 2 313
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,523评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,364评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,755评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,024评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,297评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,721评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,918评论 2 336

推荐阅读更多精彩内容