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