[03][01][02][01] Java Hotspot虛擬機中的對象表示

1. 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);
}

2. 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;

3. 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
};

4. 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參數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章