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