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

你可能感兴趣的:(Java Hotspot虚拟机中的对象表示)