Java Hotspot虚拟机中的对象表示

Hotspot虚拟机中的对象由oopDesc类表示,本文简要分析oopDesc类及其体系。

oopDesc类

oopDesc类是Java对象在Hotspot中的基本表示,该类定义在文件hotspot/src/share/vm/oops/oop.hpp中,一部分函数的实现在oop.cpp,另一部分内联函数的实现则在oop.inline.hpp。

// oopDesc is the top baseclass for objects classes.  The {name}Desc classes describe
// the format of Java objects so the fields can be accessed from C++.
class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark;
  union _metadata {
    Klass*      _klass;
    narrowKlass _compressed_klass;
  } _metadata;

  // 省略一些代码

  // Needed for javaClasses
  address*  address_field_addr(int offset) const;

  static bool is_null(oop obj);
  static bool is_null(narrowOop obj);
  static bool is_null(Klass* obj);

  // Decode an oop pointer from a narrowOop if compressed.
  // These are overloaded for oop and narrowOop as are the other functions
  // below so that they can be called in template functions.
  static oop decode_heap_oop_not_null(oop v);
  static oop decode_heap_oop_not_null(narrowOop v);

  // 省略一些代码

  // Access to fields in a instanceOop through these methods.
  oop obj_field(int offset) const;
  volatile oop obj_field_volatile(int offset) const;
  void obj_field_put(int offset, oop value);
  void obj_field_put_raw(int offset, oop value);
  void obj_field_put_volatile(int offset, oop value);

  Metadata* metadata_field(int offset) const;
  void metadata_field_put(int offset, Metadata* value);

  jbyte byte_field(int offset) const;
  void byte_field_put(int offset, jbyte contents);

  jchar char_field(int offset) const;
  void char_field_put(int offset, jchar contents);

  jboolean bool_field(int offset) const;
  void bool_field_put(int offset, jboolean contents);

  jint int_field(int offset) const;
  void int_field_put(int offset, jint contents);

  jshort short_field(int offset) const;
  void short_field_put(int offset, jshort contents);

  jlong long_field(int offset) const;
  void long_field_put(int offset, jlong contents);

  jfloat float_field(int offset) const;
  void float_field_put(int offset, jfloat contents);

  jdouble double_field(int offset) const;
  void double_field_put(int offset, jdouble contents);

  address address_field(int offset) const;
  void address_field_put(int offset, address contents);
  // 省略一些代码
}
  • _mark字段是mark word,_metadata是类指针klass pointer,对象头(object header)即是由这两个字段组成,这些术语可以参考Hotspot术语表,OpenJDK的Wiki指出这两个字段的用途:

In the Java HotSpot™ VM, every object is preceded by a class pointer and a header word. The header word, which stores the identity hash code as well as age and marking bits for generational garbage collection, is also used to implement a thin lock scheme.

  • 以decode和encode开头的函数均与对象指针的解压缩和压缩有关;
  • 以field结尾的函数可以看成某个Java对象的某种类型成员变量的get方法,而以field_put结尾的函数则可以看成某个Java对象的某种类型成员变量的set方法,这些方法在文件hotspot/src/share/vm/classfile/javaClasses.cpp中使用较多。以下面两个函数为例,is_daemon利用bool_field函数和daemon成员变量在java.lang.Thread类中的偏移量获取Thread对象中daemon成员变量的值,set_daemon则利用bool_field_put函数和daemon成员变量的偏移量将Thread对象中daemon成员变量设置为true。
bool java_lang_Thread::is_daemon(oop java_thread) {
  return java_thread->bool_field(_daemon_offset) != 0;
}

void java_lang_Thread::set_daemon(oop java_thread) {
  java_thread->bool_field_put(_daemon_offset, true);
}

oopDesc的类体系

注意上面代码中类注释的内容,oopDesc只是一个基类,{name}Desc描述了具体不同类型Java对象的格式:

  • instanceOopDesc:描述创建的实例;
  • arrayOopDesc:描述数组;
  • objArrayOopDesc:描述对象数组;
  • typeArrayOopDesc:描述Java原生类型(char、boolean、byte、short、int、long、float和double)的数组。

在源码中更常见的是这些类的指针类型,文件hotspot/src/share/vm/oops/oopsHierarchy.hpp中利用typedef定义了这些指针类型,oop是ordinary object pointer的简称:

typedef class oopDesc* oop;
typedef class instanceOopDesc* instanceOop;
typedef class arrayOopDesc* arrayOop;
typedef class objArrayOopDesc* objArrayOop;
typedef class typeArrayOopDesc* typeArrayOop;

markOopDesc类

oopDesc类的_mark字段(即mark word)是markOop类型,它是指向markOopDesc类的指针,markOopDesc类定义在文件hotspot/src/share/vm/oops/markOop.hpp中。虽然markOop是指针类型,但实际使用时只是将它当作一个字而不是一个oop,即不会去对markOop解引用。markOop在不同的使用场景中有不同的含义,这里暂时忽略与GC和压缩指针有关的内容。

  • 32位机器上mark word的各比特含义如下表:
|   使用场景   |       23位       |  2位  |  4位 |     1位  |    2位  |
|   普通对象   |     25位哈希             | 年龄 | 偏向锁标识 | 锁定状态 |
|偏向锁定的对象|线程指针JavaThread*| epoch | 年龄 | 偏向锁标识 | 锁定状态 |
  • 64位机器上mark word的各比特含义如下表:
|   使用场景   |       54位         |  2位  |  1位  |  4位 |     1位  |    2位  |
|   普通对象   |  高25位未使用,低31位保存哈希 | 未使用 | 年龄 | 偏向锁标识 | 锁定状态 |
|偏向锁定的对象|  线程指针JavaThread* | epoch| 未使用 | 年龄 | 偏向锁标识 | 锁定状态 |
  • 不同状态下mark word的组成如下表:
          最高有效位                                    最低有效位
偏向锁启用状态:| 线程指针JavaThread* | epoch | 年龄 | 1 | 01 |
 轻量锁定状态: |            指向栈上锁记录的指针         | 00 |
    无锁状态: |          25位哈希           | 年龄 | 0 | 01 |
  监视器锁状态:|             指向监视器的指针           | 10 |
   标记状态:  |  只在Mark Sweep GC中用到,其他时刻无效   | 11 |

markOopDesc类中的的枚举值验证了上表的描述:

enum { locked_value             = 0,
       unlocked_value           = 1,
       monitor_value            = 2,
       marked_value             = 3,
       biased_lock_pattern      = 5
};

Handle类

Handle类定义在文件hotspot/src/share/vm/runtime/handles.hpp中,只有一个成员变量保存oop指针,因此Handle(句柄)只是对oop的间接引用。

class Handle {
 private:
  oop* _handle;

 protected:
  oop     obj() const                            { return _handle == NULL ? (oop)NULL : *_handle; }
  oop     non_null_obj() const                   { assert(_handle != NULL, "resolving NULL handle"); return *_handle; }

 public:
  // Constructors
  Handle()                                       { _handle = NULL; }
  Handle(oop obj);
  Handle(Thread* thread, oop obj);

  // General access
  oop     operator () () const                   { return obj(); }
  oop     operator -> () const                   { return non_null_obj(); }
  bool    operator == (oop o) const              { return obj() == o; }
  bool    operator == (const Handle& h) const          { return obj() == h.obj(); }

  // Null checks
  bool    is_null() const                        { return _handle == NULL; }
  bool    not_null() const                       { return _handle != NULL; }

  // Debugging
  void    print()                                { obj()->print(); }

  // Direct interface, use very sparingly.
  // Used by JavaCalls to quickly convert handles and to create handles static data structures.
  // Constructor takes a dummy argument to prevent unintentional type conversion in C++.
  Handle(oop *handle, bool dummy)                { _handle = handle; }

  // Raw handle access. Allows easy duplication of Handles. This can be very unsafe
  // since duplicates is only valid as long as original handle is alive.
  oop* raw_value()                               { return _handle; }
  static oop raw_resolve(oop *handle)            { return handle == NULL ? (oop)NULL : *handle; }
};

Handle类的构造函数在文件hotspot/src/share/vm/runtime/handles.inline.hpp中实现,代码如下所示。

inline Handle::Handle(oop obj) {
  if (obj == NULL) {
    _handle = NULL;
  } else {
    _handle = Thread::current()->handle_area()->allocate_handle(obj);
  }
}

inline Handle::Handle(Thread* thread, oop obj) {
  assert(thread == Thread::current(), "sanity check");
  if (obj == NULL) {
    _handle = NULL;
  } else {
    _handle = thread->handle_area()->allocate_handle(obj);
  }
}

Thread::current()函数用于获取当前线程的线程指针(参见后续线程启动分析的文章),这两个构造函数都为线程分配了属于该线程的句柄以引用obj参数。

参考文献

https://openjdk.java.net/groups/hotspot/docs/FOSDEM-2007-HotSpot.pdf

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

推荐阅读更多精彩内容