JAVA是面向对象语言,它的一个重要概念就是面向对象,而其所有类都继承于Object类,所有对象都实现这个类的方法。可以说,它的存在使得所有类都有了共同的祖先,接下来我们从源码来研究下这个神奇的Object类(本文的研究基于jdk-9.0.1)。
1、安装jdk-9.0.1,从jdk目录下的lib目录里可以找到src.zip,解压后可以得到javase9的大部分源代码。
2、安装eclipse,这里我安装的是eclipse 4.7(Oxygen)版本,它支持JDK9。打开eclipse后新建一个普通Java工程,将源代码拖曳进工程,可以得到整个代码结构如下图:
3、想获得更完整的源代码,也可以下载openJdk9,地址是http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/65464a307408。
4、下载HotsPot VM源代码,地址是http://hg.openjdk.java.net/jdk9/jdk9/hotspot/file/b756e7a2ec33。
首先我们打开src/java.base/java/lang/Object.java,开篇的注释简明扼要阐述了该类的作用:“Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.”由此可见,Object类在JAVA中的重要性是非常大的。
Object类的API如下:
接下来我们一个个地进行解读。
Java中规定:在类定义过程中,对于未定义构造函数的类,默认会有一个无参数的构造函数,Object类作为所有类的基类,当然也会有自己的构造方法。其实在jdk9之前的版本中,这个构造函数都是没有显式写出来的,而我们可以看到从这个版本开始,构造函数被显式写出。
源码:
@HotSpotIntrinsicCandidate
public Object() {}
源码:
private static native void registerNatives();
可以发现,这个registerNatives方法用了native修饰符,而且找不到它的实现,那么它到底是干嘛的,怎么工作的呢?别急,接下来我们慢慢研究。
修饰符native表明了这是个本地方法,那么什么是本地方法?
我们知道JAVA是无法直接访问到操作系统底层(如系统硬件等) 的,当代码中需要访问到底层时,就需要用native方法来扩展了,它能够通过JNI接口调用其他语言来实现对底层的访问。关于JNI的介绍,大家可以直接度娘,本文就先不做详细介绍了。
我们看回registerNatives方法,通过上面的介绍,我们知道它的实现将是由外部的C/C++代码去完成。但是细看会发现这个方法前面还有其他的关键字,其中还有private,那么它是怎么被执行的?其实,在源码中,该方法声明的后面还紧接着一段静态代码段:
static {
registerNatives();
}
也就是说当Object加载进JVM时,registerNatives方法就会被调用执行了。
为了探究这个方法究竟是如何实现的,接下来顺着源码来看看。
通过上面的研究,Object类会去调用本地方法,这些方法存放在Object.c中,在openJdk9中我们可以看到这个c文件的源码(src\java.base\share\native\libjava\Object.c):
#include
#include
#include
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "java_lang_Object.h"
static JNINativeMethod methods[] = {
{"hashCode", "()I", (void *)&JVM_IHashCode},
{"wait", "(J)V", (void *)&JVM_MonitorWait},
{"notify", "()V", (void *)&JVM_MonitorNotify},
{"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll},
{"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone},
};
JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0]));
}
JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
{
if (this == NULL) {
JNU_ThrowNullPointerException(env, NULL);
return 0;
} else {
return (*env)->GetObjectClass(env, this);
}
}
根据命名规则,我们知道registerNatives方法会调用Object.c中的Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)方法,去实现它所要做的事情。
通过阅读代码,可以看到这个方法调用了JNIEnv的RegisterNatives()方法,追溯上去,可以发现这个方法定义在jni.h中(src\java.base\share\native\include\jni.h)。通过语句:
struct JNIEnv_;
声明了JNIEnv结构体,并在之后的代码中定义了其中的各种方法,我们要找的RegisterNatives()方法就在其中:
...
jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods) {
return functions->RegisterNatives(this,clazz,methods,nMethods);
}
...
阅读发现,方法调用了JNINativeInterface的RegisterNatives()方法。通过语句:
const struct JNINativeInterface_ *functions;
声明了JNINativeInterface结构体的引用,而其结构方法也在代码中有所定义,在其中我们可以找到对RegisterNatives的定义:
...
jint (JNICALL *RegisterNatives)
(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
...
至此,程序调用JNI提供的RegisterNatives函数,将本地函数注册到JVM中。这个函数在HotsPot源码的jni.cpp中(src\share\vm\prims)实现。代码是:
JNI_ENTRY(jint, jni_RegisterNatives(JNIEnv *env, jclass clazz,
const JNINativeMethod *methods,
jint nMethods))
JNIWrapper("RegisterNatives");
HOTSPOT_JNI_REGISTERNATIVES_ENTRY(env, clazz, (void *) methods, nMethods);
jint ret = 0;
DT_RETURN_MARK(RegisterNatives, jint, (const jint&)ret);
KlassHandle h_k(thread, java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)));
for (int index = 0; index < nMethods; index++) {
const char* meth_name = methods[index].name;
const char* meth_sig = methods[index].signature;
int meth_name_len = (int)strlen(meth_name);
// The class should have been loaded (we have an instance of the class
// passed in) so the method and signature should already be in the symbol
// table. If they're not there, the method doesn't exist.
TempNewSymbol name = SymbolTable::probe(meth_name, meth_name_len);
TempNewSymbol signature = SymbolTable::probe(meth_sig, (int)strlen(meth_sig));
if (name == NULL || signature == NULL) {
ResourceMark rm;
stringStream st;
st.print("Method %s.%s%s not found", h_k()->external_name(), meth_name, meth_sig);
// Must return negative value on failure
THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), -1);
}
bool res = register_native(h_k, name, signature,
(address) methods[index].fnPtr, THREAD);
if (!res) {
ret = -1;
break;
}
}
return ret;
JNI_END
通过阅读代码,可以找到注册方法的主要函数是register_native(),它的实现是:
static bool register_native(KlassHandle k, Symbol* name, Symbol* signature, address entry, TRAPS) {
Method* method = k()->lookup_method(name, signature);
if (method == NULL) {
ResourceMark rm;
stringStream st;
st.print("Method %s name or signature does not match",
Method::name_and_sig_as_C_string(k(), name, signature));
THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false);
}
if (!method->is_native()) {
// trying to register to a non-native method, see if a JVM TI agent has added prefix(es)
method = find_prefixed_native(k, name, signature, THREAD);
if (method == NULL) {
ResourceMark rm;
stringStream st;
st.print("Method %s is not declared as native",
Method::name_and_sig_as_C_string(k(), name, signature));
THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false);
}
}
if (entry != NULL) {
method->set_native_function(entry,
Method::native_bind_event_is_interesting);
} else {
method->clear_native_function();
}
if (PrintJNIResolving) {
ResourceMark rm(THREAD);
tty->print_cr("[Registering JNI native method %s.%s]",
method->method_holder()->external_name(),
method->name()->as_C_string());
}
return true;
}
其中又可以看到调用了函数set_native_function(),它是在method.cpp(src\share\vm\oops)中实现的。代码:
void Method::set_native_function(address function, bool post_event_flag) {
assert(function != NULL, "use clear_native_function to unregister natives");
assert(!is_method_handle_intrinsic() || function == SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), "");
address* native_function = native_function_addr();
// We can see racers trying to place the same native function into place. Once
// is plenty.
address current = *native_function;
if (current == function) return;
if (post_event_flag && JvmtiExport::should_post_native_method_bind() &&
function != NULL) {
// native_method_throw_unsatisfied_link_error_entry() should only
// be passed when post_event_flag is false.
assert(function !=
SharedRuntime::native_method_throw_unsatisfied_link_error_entry(),
"post_event_flag mis-match");
// post the bind event, and possible change the bind function
JvmtiExport::post_native_method_bind(this, &function);
}
*native_function = function;
// This function can be called more than once. We must make sure that we always
// use the latest registered method -> check if a stub already has been generated.
// If so, we have to make it not_entrant.
CompiledMethod* nm = code(); // Put it into local variable to guard against concurrent updates
if (nm != NULL) {
nm->make_not_entrant();
}
}
其中可以看到最终注册本地方法用的是JvmtiExport::post_native_method_bind()函数,它在jvmtiExport.cpp中(src\share\vm\prims)实现。代码是:
void JvmtiExport::post_native_method_bind(Method* method, address* function_ptr) {
JavaThread* thread = JavaThread::current();
assert(thread->thread_state() == _thread_in_vm, "must be in vm state");
HandleMark hm(thread);
methodHandle mh(thread, method);
EVT_TRIG_TRACE(JVMTI_EVENT_NATIVE_METHOD_BIND, ("[%s] Trg Native Method Bind event triggered",
JvmtiTrace::safe_get_thread_name(thread)));
if (JvmtiEventController::is_enabled(JVMTI_EVENT_NATIVE_METHOD_BIND)) {
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
if (env->is_enabled(JVMTI_EVENT_NATIVE_METHOD_BIND)) {
EVT_TRACE(JVMTI_EVENT_NATIVE_METHOD_BIND, ("[%s] Evt Native Method Bind event sent",
JvmtiTrace::safe_get_thread_name(thread) ));
JvmtiMethodEventMark jem(thread, mh);
JvmtiJavaThreadEventTransition jet(thread);
JNIEnv* jni_env = (env->phase() == JVMTI_PHASE_PRIMORDIAL) ? NULL : jem.jni_env();
jvmtiEventNativeMethodBind callback = env->callbacks()->NativeMethodBind;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jni_env, jem.jni_thread(),
jem.jni_methodID(), (void*)(*function_ptr), (void**)function_ptr);
}
}
}
}
}
知道了JAVA是如何通过JNI与本地底层打交道之后,我们也可以自己编写native方法,这个是后话,不在本篇讨论。
源码:
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
从源码可以看出,clone()也是一个本地方法,在上一小节中,我们在Object.c文件中看到了它被定义到一个JNINativeMethod数组中:
static JNINativeMethod methods[] = {
{"hashCode", "()I", (void *)&JVM_IHashCode},
{"wait", "(J)V", (void *)&JVM_MonitorWait},
{"notify", "()V", (void *)&JVM_MonitorNotify},
{"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll},
{"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone},
};
由此可见,它的实现也是由JNI来处理。JNINativeMethod的结构体声明在jni.h中:
/*
* used in RegisterNatives to describe native method name, signature,
* and function pointer.
*/
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
回头看clone的函数指针,它指向了JVM_Clone,我们在jvm.h(jdk9\src\java.base\share\native\include\jvm.h)中可以看到它的声明:
JNIEXPORT jobject JNICALL
JVM_Clone(JNIEnv *env, jobject obj);
而关于它的实现在jvm.cpp(hotspot-jdk9\src\share\vm\prims\jvm.cpp)中可以找到:
JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
JVMWrapper("JVM_Clone");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
const KlassHandle klass (THREAD, obj->klass());
JvmtiVMObjectAllocEventCollector oam;
#ifdef ASSERT
// Just checking that the cloneable flag is set correct
if (obj->is_array()) {
guarantee(klass->is_cloneable(), "all arrays are cloneable");
} else {
guarantee(obj->is_instance(), "should be instanceOop");
bool cloneable = klass->is_subtype_of(SystemDictionary::Cloneable_klass());
guarantee(cloneable == klass->is_cloneable(), "incorrect cloneable flag");
}
#endif
// Check if class of obj supports the Cloneable interface.
// All arrays are considered to be cloneable (See JLS 20.1.5)
if (!klass->is_cloneable()) {
ResourceMark rm(THREAD);
THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());
}
// Make shallow object copy
const int size = obj->size();
oop new_obj_oop = NULL;
if (obj->is_array()) {
const int length = ((arrayOop)obj())->length();
new_obj_oop = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);
} else {
new_obj_oop = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);
}
// 4839641 (4840070): We must do an oop-atomic copy, because if another thread
// is modifying a reference field in the clonee, a non-oop-atomic copy might
// be suspended in the middle of copying the pointer and end up with parts
// of two different pointers in the field. Subsequent dereferences will crash.
// 4846409: an oop-copy of objects with long or double fields or arrays of same
// won't copy the longs/doubles atomically in 32-bit vm's, so we copy jlongs instead
// of oops. We know objects are aligned on a minimum of an jlong boundary.
// The same is true of StubRoutines::object_copy and the various oop_copy
// variants, and of the code generated by the inline_native_clone intrinsic.
assert(MinObjAlignmentInBytes >= BytesPerLong, "objects misaligned");
Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj_oop,
(size_t)align_object_size(size) / HeapWordsPerLong);
// Clear the header
new_obj_oop->init_mark();
// Store check (mark entire object and let gc sort it out)
BarrierSet* bs = Universe::heap()->barrier_set();
assert(bs->has_write_region_opt(), "Barrier set does not have write_region");
bs->write_region(MemRegion((HeapWord*)new_obj_oop, size));
Handle new_obj(THREAD, new_obj_oop);
// Special handling for MemberNames. Since they contain Method* metadata, they
// must be registered so that RedefineClasses can fix metadata contained in them.
if (java_lang_invoke_MemberName::is_instance(new_obj()) &&
java_lang_invoke_MemberName::is_method(new_obj())) {
Method* method = (Method*)java_lang_invoke_MemberName::vmtarget(new_obj());
// MemberName may be unresolved, so doesn't need registration until resolved.
if (method != NULL) {
methodHandle m(THREAD, method);
// This can safepoint and redefine method, so need both new_obj and method
// in a handle, for two different reasons. new_obj can move, method can be
// deleted if nothing is using it on the stack.
m->method_holder()->add_member_name(new_obj(), false);
}
}
// Caution: this involves a java upcall, so the clone should be
// "gc-robust" by this stage.
if (klass->has_finalizer()) {
assert(obj->is_instance(), "should be instanceOop");
new_obj_oop = InstanceKlass::register_finalizer(instanceOop(new_obj()), CHECK_NULL);
new_obj = Handle(THREAD, new_obj_oop);
}
return JNIHandles::make_local(env, new_obj());
JVM_END
代码会首先判断准备被clone的类是否实现了Cloneable接口,若为否则会抛CloneNotSupportException异常。接着根据要clone的对象是否数组进行新对象的内存分配以及信息写入,然后copy内存块信息到新对象。接下来初始化对象头,进行存储检查标记新对象分配堆栈,然后将需要特别注册的方法进行注册,最后将复制完成的内存块转换成本地对象并将其返回。
这里主要执行copy的代码是:
...
Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj_oop,
(size_t)align_object_size(size) / HeapWordsPerLong);
...
这个方法在hotspot-jdk9\src\share\vm\utilities\copy.hpp中实现:
static void conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) {
assert_params_ok(from, to, LogBytesPerLong);
pd_conjoint_jlongs_atomic(from, to, count);
}
方法中执行了pd_conjoint_jlongs_atomic()方法,其位于hotspot-jdk9\src\cpu\zero\vm\copy_zero.hpp中:
static void pd_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) {
_Copy_conjoint_jlongs_atomic(from, to, count);
}
再接着执行_Copy_conjoint_jlongs_atomic()方法,在hotspot-jdk9\src\os_cpu\linux_zero\vm\os_linux_zero.cpp中:
void _Copy_conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) {
if (from > to) {
jlong *end = from + count;
while (from < end)
os::atomic_copy64(from++, to++);
}
else if (from < to) {
jlong *end = from;
from += count - 1;
to += count - 1;
while (from >= end)
os::atomic_copy64(from--, to--);
}
}
至此,我们看到了整个对象copy的过程,就是把from指针指向的内存的值赋给to指针指向的内存,这是一个简单的拷贝操作。可以知道,在经过了clone()方法生成的新对象并不是通过构造函数来创建,而是直接在内存层面进行了copy操作。
要注意的是,使用clone()来进行对象的copy是浅拷贝,关于浅拷贝和深拷贝这里就先不进行讨论了。
源码:
public boolean equals(Object obj) {
return (this == obj);
}
equals()方法用于比较其他对象与本对象是否等同,它适用于非空对象之间的对比。从它的注释来看,它拥有以下这几个特性(自反性、对称性、传递性、一致性、非空性):
从代码中我们可以看到,默认的equals()实现采用的是等价符,也就是说如果不重写该方法,则equals()与“==”是等效的,所以一般在需要时,我们会重写类的equals()方法,但一定要谨记要遵守它的5个特性。另外,如果是基本数据类型之间的比较,“==”比较的是数值,而如果是复合数据类型之间的比较,则它比较的是对象的引用地址是否相等。
源码:
@Deprecated(since="9")
protected void finalize() throws Throwable { }
finalize()方法在对象终结时调用,它在GC回收对象时会自动被调用,但JVM不保证finalize()方法一定会被调用,也就是说它的自动调用是不确定的。当然,基于这个原因,当初SUN就不提倡大家使用这个方法。现在我们看到再JDK9中,这个方法终于被标记为Deprecated,即为过时方法,从注释中也可以看出,Oracle建议用java.lang.ref.Cleaner来替代finalize()的使用。
源码:
@HotSpotIntrinsicCandidate
public final native Class> getClass();
与registerNatives()一样,getClass()也是一个native方法,自然我们还得从Object.c中去寻找痕迹:
…
JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
{
if (this == NULL) {
JNU_ThrowNullPointerException(env, NULL);
return 0;
} else {
return (*env)->GetObjectClass(env, this);
}
}
…
往下查找,GetObjectClass()这个方法在jni.h中定义:
jclass GetObjectClass(jobject obj) {
return functions->GetObjectClass(this,obj);
}
它的实现在jni.cpp中:
JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj))
JNIWrapper("GetObjectClass");
HOTSPOT_JNI_GETOBJECTCLASS_ENTRY(env, obj);
Klass* k = JNIHandles::resolve_non_null(obj)->klass();
jclass ret =
(jclass) JNIHandles::make_local(env, k->java_mirror());
HOTSPOT_JNI_GETOBJECTCLASS_RETURN(ret);
return ret;
JNI_END
综合以上代码,可以看到JVM返回了对象运行时的类。GetClass()是一个类的实例所具备的方法,它是在运行时才确定的,所以若此时实例终止了,则会抛出空指针异常。
源码:
@HotSpotIntrinsicCandidate
public native int hashCode();
是的,又是一个native方法,它和clone()一样,定义在Object.c的JNINativeMethod数组里,它的函数指针指向了JVM_IHashCode,我们在jvm.h中可以看到它的声明:
JNIEXPORT jint JNICALL
JVM_IHashCode(JNIEnv *env, jobject obj);
同样,在jvm.cpp中可以看到实现:
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))
JVMWrapper("JVM_IHashCode");
// as implemented in the classic virtual machine; return 0 if object is NULL
return handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END
看来hashCode最终是通过ObjectSynchronizer::FastHashCode ()方法获取的,它的实现在hotspot\src\share\vm\runtime\synchronizer.cpp中:
intptr_t ObjectSynchronizer::FastHashCode(Thread * Self, oop obj) {
if (UseBiasedLocking) {
// NOTE: many places throughout the JVM do not expect a safepoint
// to be taken here, in particular most operations on perm gen
// objects. However, we only ever bias Java instances and all of
// the call sites of identity_hash that might revoke biases have
// been checked to make sure they can handle a safepoint. The
// added check of the bias pattern is to avoid useless calls to
// thread-local storage.
if (obj->mark()->has_bias_pattern()) {
// Handle for oop obj in case of STW safepoint
Handle hobj(Self, obj);
// Relaxing assertion for bug 6320749.
assert(Universe::verify_in_progress() ||
!SafepointSynchronize::is_at_safepoint(),
"biases should not be seen by VM thread here");
BiasedLocking::revoke_and_rebias(hobj, false, JavaThread::current());
obj = hobj();
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
}
}
// hashCode() is a heap mutator ...
// Relaxing assertion for bug 6320749.
assert(Universe::verify_in_progress() || DumpSharedSpaces ||
!SafepointSynchronize::is_at_safepoint(), "invariant");
assert(Universe::verify_in_progress() || DumpSharedSpaces ||
Self->is_Java_thread() , "invariant");
assert(Universe::verify_in_progress() || DumpSharedSpaces ||
((JavaThread *)Self)->thread_state() != _thread_blocked, "invariant");
ObjectMonitor* monitor = NULL;
markOop temp, test;
intptr_t hash;
markOop mark = ReadStableMark(obj);
// object should remain ineligible for biased locking
assert(!mark->has_bias_pattern(), "invariant");
if (mark->is_neutral()) {
hash = mark->hash(); // this is a normal header
if (hash) { // if it has hash, just return it
return hash;
}
hash = get_next_hash(Self, obj); // allocate a new hash code
temp = mark->copy_set_hash(hash); // merge the hash code into header
// use (machine word version) atomic operation to install the hash
test = (markOop) Atomic::cmpxchg_ptr(temp, obj->mark_addr(), mark);
if (test == mark) {
return hash;
}
// If atomic operation failed, we must inflate the header
// into heavy weight monitor. We could add more code here
// for fast path, but it does not worth the complexity.
} else if (mark->has_monitor()) {
monitor = mark->monitor();
temp = monitor->header();
assert(temp->is_neutral(), "invariant");
hash = temp->hash();
if (hash) {
return hash;
}
// Skip to the following code to reduce code size
} else if (Self->is_lock_owned((address)mark->locker())) {
temp = mark->displaced_mark_helper(); // this is a lightweight monitor owned
assert(temp->is_neutral(), "invariant");
hash = temp->hash(); // by current thread, check if the displaced
if (hash) { // header contains hash code
return hash;
}
// WARNING:
// The displaced header is strictly immutable.
// It can NOT be changed in ANY cases. So we have
// to inflate the header into heavyweight monitor
// even the current thread owns the lock. The reason
// is the BasicLock (stack slot) will be asynchronously
// read by other threads during the inflate() function.
// Any change to stack may not propagate to other threads
// correctly.
}
// Inflate the monitor to set hash code
monitor = ObjectSynchronizer::inflate(Self, obj, inflate_cause_hash_code);
// Load displaced header and check it has hash code
mark = monitor->header();
assert(mark->is_neutral(), "invariant");
hash = mark->hash();
if (hash == 0) {
hash = get_next_hash(Self, obj);
temp = mark->copy_set_hash(hash); // merge hash code into header
assert(temp->is_neutral(), "invariant");
test = (markOop) Atomic::cmpxchg_ptr(temp, monitor, mark);
if (test != mark) {
// The only update to the header in the monitor (outside GC)
// is install the hash code. If someone add new usage of
// displaced header, please update this code
hash = test->hash();
assert(test->is_neutral(), "invariant");
assert(hash != 0, "Trivial unexpected object/monitor header usage.");
}
}
// We finally get the hash
return hash;
}
从代码中我们知道,hash是从markOop对象的hash()方法中获取的,这个方法的实现在hotspot\src\share\vm\oops\markOop.hpp中:
// hash operations
intptr_t hash() const {
return mask_bits(value() >> hash_shift, hash_mask);
}
从整体来看,hashCode就是根据对象的地址或者字符串或者数字算出来的int类型的数值。
源码:
@HotSpotIntrinsicCandidate
public final native void notify();
这个方法用来唤醒一个在当前对象监视器(monitor)里的正等待唤醒的线程,而且notify()只能在本身拥有对应对象监视器的对象上去调用,获得对象本身的监视器有三种途径:
也就是从执行该对象的同步方法中获取、从执行该对象的同步块中获取、从执行类的静态同步方法中获取。
从源码分析,我们依然还是在Object.c中可以找到它的定义,同样在JNINativeMethod数组里,它的函数指针指向JVM_MonitorNotify。我们再从jvm.h中可以看到定义:
JNIEXPORT void JNICALL JVM_MonitorNotify(JNIEnv *env, jobject obj);
它的实现在jvm.cpp中:
JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))
JVMWrapper("JVM_MonitorNotify");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
ObjectSynchronizer::notify(obj, CHECK);
JVM_END
在代码中,主要的实现是调用了ObjectSynchronizer::notify()函数,我们在synchronize.cpp中可以找到它的实现:
void ObjectSynchronizer::notify(Handle obj, TRAPS) {
if (UseBiasedLocking) {
BiasedLocking::revoke_and_rebias(obj, false, THREAD);
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
}
markOop mark = obj->mark();
if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
return;
}
ObjectSynchronizer::inflate(THREAD,
obj(),
inflate_cause_notify)->notify(THREAD);
}
通过ObjectSynchronizer::inflate()方法获得了objectMonitor对象,执行其中的notify()方法,该方法在hotspot\src\share\vm\runtime\objectMonitor.cpp中实现:
// Consider: a not-uncommon synchronization bug is to use notify() when
// notifyAll() is more appropriate, potentially resulting in stranded
// threads; this is one example of a lost wakeup. A useful diagnostic
// option is to force all notify() operations to behave as notifyAll().
//
// Note: We can also detect many such problems with a "minimum wait".
// When the "minimum wait" is set to a small non-zero timeout value
// and the program does not hang whereas it did absent "minimum wait",
// that suggests a lost wakeup bug. The '-XX:SyncFlags=1' option uses
// a "minimum wait" for all park() operations; see the recheckInterval
// variable and MAX_RECHECK_INTERVAL.
void ObjectMonitor::notify(TRAPS) {
CHECK_OWNER();
if (_WaitSet == NULL) {
TEVENT(Empty-Notify);
return;
}
DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
INotify(THREAD);
OM_PERFDATA_OP(Notifications, inc(1));
}
如此,线程被唤醒。
源码:
@HotSpotIntrinsicCandidate
public final native void notifyAll();
与notify()方法类似,notifyAll()方法也是唤醒线程,但它唤醒的是全部等待唤醒的线程。
从源码分析,我们依然还是在Object.c中可以找到它的定义,同样在JNINativeMethod数组里,它的函数指针指向JVM_MonitorNotifyAll。我们再从jvm.h中可以看到定义:
JNIEXPORT void JNICALL
JVM_MonitorNotifyAll(JNIEnv *env, jobject obj);
它的实现在jvm.cpp中:
JVM_ENTRY(void, JVM_MonitorNotifyAll(JNIEnv* env, jobject handle))
JVMWrapper("JVM_MonitorNotifyAll");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
ObjectSynchronizer::notifyall(obj, CHECK);
JVM_END
在代码中,主要的实现是调用了ObjectSynchronizer::notifyall()函数,我们在synchronize.cpp中可以找到它的实现:
void ObjectSynchronizer::notifyall(Handle obj, TRAPS) {
if (UseBiasedLocking) {
BiasedLocking::revoke_and_rebias(obj, false, THREAD);
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
}
markOop mark = obj->mark();
if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {
return;
}
ObjectSynchronizer::inflate(THREAD,
obj(),
inflate_cause_notify)->notifyAll(THREAD);
}
通过ObjectSynchronizer::inflate()方法获得了objectMonitor对象,执行其中的notifyAll()方法,该方法在hotspot\src\share\vm\runtime\objectMonitor.cpp中实现:
// The current implementation of notifyAll() transfers the waiters one-at-a-time
// from the waitset to the EntryList. This could be done more efficiently with a
// single bulk transfer but in practice it's not time-critical. Beware too,
// that in prepend-mode we invert the order of the waiters. Let's say that the
// waitset is "ABCD" and the EntryList is "XYZ". After a notifyAll() in prepend
// mode the waitset will be empty and the EntryList will be "DCBAXYZ".
void ObjectMonitor::notifyAll(TRAPS) {
CHECK_OWNER();
if (_WaitSet == NULL) {
TEVENT(Empty-NotifyAll);
return;
}
DTRACE_MONITOR_PROBE(notifyAll, this, object(), THREAD);
int tally = 0;
while (_WaitSet != NULL) {
tally++;
INotify(THREAD);
}
OM_PERFDATA_OP(Notifications, inc(tally));
}
源码:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
toString()方法返回的是一串用文本形式来描述这个对象的字符串,它应该尽量简洁但也要能体现丰富的信息,便于阅读。注释中建议我们在所有子类中重写此方法。
从代码中可以看到,Object类的toString()方法返回的是该类的对象的实例名和用16进制表示的该对象的地址(哈希码),中间用“@”隔开。
源码:
public final void wait() throws InterruptedException {
wait(0);
}
wait()方法用来让当前线程处于等待状态,直到线程被notify()或者notifyAll()唤醒。不带参数的wait()相当于执行了wait(0)。
源码:
public final native void wait(long timeout) throws InterruptedException;
可以看到,带一个参数的wait()方法是native方法,因此我们从Object.c去找它。同样的,它也在JNINativeMethod数组中定义,函数指针指向了JVM_MonitorWait。从jvm.h中可以看到:
JNIEXPORT void JNICALL
JVM_MonitorWait(JNIEnv *env, jobject obj, jlong ms);
方法的实现在jvm.cpp中:
JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))
JVMWrapper("JVM_MonitorWait");
Handle obj(THREAD, JNIHandles::resolve_non_null(handle));
JavaThreadInObjectWaitState jtiows(thread, ms != 0);
if (JvmtiExport::should_post_monitor_wait()) {
JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);
// The current thread already owns the monitor and it has not yet
// been added to the wait queue so the current thread cannot be
// made the successor. This means that the JVMTI_EVENT_MONITOR_WAIT
// event handler cannot accidentally consume an unpark() meant for
// the ParkEvent associated with this ObjectMonitor.
}
ObjectSynchronizer::wait(obj, ms, CHECK);
JVM_END
其中主要是执行了ObjectSynchronizer::wait()方法,在synchronize.cpp中可以找到:
// Wait/Notify/NotifyAll
// NOTE: must use heavy weight monitor to handle wait()
int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
if (UseBiasedLocking) {
BiasedLocking::revoke_and_rebias(obj, false, THREAD);
assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");
}
if (millis < 0) {
TEVENT(wait - throw IAX);
THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
}
ObjectMonitor* monitor = ObjectSynchronizer::inflate(THREAD,
obj(),
inflate_cause_wait);
DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis);
monitor->wait(millis, true, THREAD);
// This dummy call is in place to get around dtrace bug 6254741. Once
// that's fixed we can uncomment the following line, remove the call
// and change this function back into a "void" func.
// DTRACE_MONITOR_PROBE(waited, monitor, obj(), THREAD);
return dtrace_waited_probe(monitor, obj, THREAD);
}
wait()的执行最终是在ObjectMonitor.cpp中的实现的:
// Wait/Notify/NotifyAll
//
// Note: a subset of changes to ObjectMonitor::wait()
// will need to be replicated in complete_exit
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
Thread * const Self = THREAD;
assert(Self->is_Java_thread(), "Must be Java thread!");
JavaThread *jt = (JavaThread *)THREAD;
DeferredInitialize();
// Throw IMSX or IEX.
CHECK_OWNER();
EventJavaMonitorWait event;
// check for a pending interrupt
if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) {
// post monitor waited event. Note that this is past-tense, we are done waiting.
if (JvmtiExport::should_post_monitor_waited()) {
// Note: 'false' parameter is passed here because the
// wait was not timed out due to thread interrupt.
JvmtiExport::post_monitor_waited(jt, this, false);
// In this short circuit of the monitor wait protocol, the
// current thread never drops ownership of the monitor and
// never gets added to the wait queue so the current thread
// cannot be made the successor. This means that the
// JVMTI_EVENT_MONITOR_WAITED event handler cannot accidentally
// consume an unpark() meant for the ParkEvent associated with
// this ObjectMonitor.
}
if (event.should_commit()) {
post_monitor_wait_event(&event, 0, millis, false);
}
TEVENT(Wait - Throw IEX);
THROW(vmSymbols::java_lang_InterruptedException());
return;
}
TEVENT(Wait);
assert(Self->_Stalled == 0, "invariant");
Self->_Stalled = intptr_t(this);
jt->set_current_waiting_monitor(this);
// create a node to be put into the queue
// Critically, after we reset() the event but prior to park(), we must check
// for a pending interrupt.
ObjectWaiter node(Self);
node.TState = ObjectWaiter::TS_WAIT;
Self->_ParkEvent->reset();
OrderAccess::fence(); // ST into Event; membar ; LD interrupted-flag
// Enter the waiting queue, which is a circular doubly linked list in this case
// but it could be a priority queue or any data structure.
// _WaitSetLock protects the wait queue. Normally the wait queue is accessed only
// by the the owner of the monitor *except* in the case where park()
// returns because of a timeout of interrupt. Contention is exceptionally rare
// so we use a simple spin-lock instead of a heavier-weight blocking lock.
Thread::SpinAcquire(&_WaitSetLock, "WaitSet - add");
AddWaiter(&node);
Thread::SpinRelease(&_WaitSetLock);
if ((SyncFlags & 4) == 0) {
_Responsible = NULL;
}
intptr_t save = _recursions; // record the old recursion count
_waiters++; // increment the number of waiters
_recursions = 0; // set the recursion level to be 1
exit(true, Self); // exit the monitor
guarantee(_owner != Self, "invariant");
// The thread is on the WaitSet list - now park() it.
// On MP systems it's conceivable that a brief spin before we park
// could be profitable.
//
// TODO-FIXME: change the following logic to a loop of the form
// while (!timeout && !interrupted && _notified == 0) park()
int ret = OS_OK;
int WasNotified = 0;
{ // State transition wrappers
OSThread* osthread = Self->osthread();
OSThreadWaitState osts(osthread, true);
{
ThreadBlockInVM tbivm(jt);
// Thread is in thread_blocked state and oop access is unsafe.
jt->set_suspend_equivalent();
if (interruptible && (Thread::is_interrupted(THREAD, false) || HAS_PENDING_EXCEPTION)) {
// Intentionally empty
} else if (node._notified == 0) {
if (millis <= 0) {
Self->_ParkEvent->park();
} else {
ret = Self->_ParkEvent->park(millis);
}
}
// were we externally suspended while we were waiting?
if (ExitSuspendEquivalent (jt)) {
// TODO-FIXME: add -- if succ == Self then succ = null.
jt->java_suspend_self();
}
} // Exit thread safepoint: transition _thread_blocked -> _thread_in_vm
// Node may be on the WaitSet, the EntryList (or cxq), or in transition
// from the WaitSet to the EntryList.
// See if we need to remove Node from the WaitSet.
// We use double-checked locking to avoid grabbing _WaitSetLock
// if the thread is not on the wait queue.
//
// Note that we don't need a fence before the fetch of TState.
// In the worst case we'll fetch a old-stale value of TS_WAIT previously
// written by the is thread. (perhaps the fetch might even be satisfied
// by a look-aside into the processor's own store buffer, although given
// the length of the code path between the prior ST and this load that's
// highly unlikely). If the following LD fetches a stale TS_WAIT value
// then we'll acquire the lock and then re-fetch a fresh TState value.
// That is, we fail toward safety.
if (node.TState == ObjectWaiter::TS_WAIT) {
Thread::SpinAcquire(&_WaitSetLock, "WaitSet - unlink");
if (node.TState == ObjectWaiter::TS_WAIT) {
DequeueSpecificWaiter(&node); // unlink from WaitSet
assert(node._notified == 0, "invariant");
node.TState = ObjectWaiter::TS_RUN;
}
Thread::SpinRelease(&_WaitSetLock);
}
// The thread is now either on off-list (TS_RUN),
// on the EntryList (TS_ENTER), or on the cxq (TS_CXQ).
// The Node's TState variable is stable from the perspective of this thread.
// No other threads will asynchronously modify TState.
guarantee(node.TState != ObjectWaiter::TS_WAIT, "invariant");
OrderAccess::loadload();
if (_succ == Self) _succ = NULL;
WasNotified = node._notified;
// Reentry phase -- reacquire the monitor.
// re-enter contended monitor after object.wait().
// retain OBJECT_WAIT state until re-enter successfully completes
// Thread state is thread_in_vm and oop access is again safe,
// although the raw address of the object may have changed.
// (Don't cache naked oops over safepoints, of course).
// post monitor waited event. Note that this is past-tense, we are done waiting.
if (JvmtiExport::should_post_monitor_waited()) {
JvmtiExport::post_monitor_waited(jt, this, ret == OS_TIMEOUT);
if (node._notified != 0 && _succ == Self) {
// In this part of the monitor wait-notify-reenter protocol it
// is possible (and normal) for another thread to do a fastpath
// monitor enter-exit while this thread is still trying to get
// to the reenter portion of the protocol.
//
// The ObjectMonitor was notified and the current thread is
// the successor which also means that an unpark() has already
// been done. The JVMTI_EVENT_MONITOR_WAITED event handler can
// consume the unpark() that was done when the successor was
// set because the same ParkEvent is shared between Java
// monitors and JVM/TI RawMonitors (for now).
//
// We redo the unpark() to ensure forward progress, i.e., we
// don't want all pending threads hanging (parked) with none
// entering the unlocked monitor.
node._event->unpark();
}
}
if (event.should_commit()) {
post_monitor_wait_event(&event, node._notifier_tid, millis, ret == OS_TIMEOUT);
}
OrderAccess::fence();
assert(Self->_Stalled != 0, "invariant");
Self->_Stalled = 0;
assert(_owner != Self, "invariant");
ObjectWaiter::TStates v = node.TState;
if (v == ObjectWaiter::TS_RUN) {
enter(Self);
} else {
guarantee(v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant");
ReenterI(Self, &node);
node.wait_reenter_end(this);
}
// Self has reacquired the lock.
// Lifecycle - the node representing Self must not appear on any queues.
// Node is about to go out-of-scope, but even if it were immortal we wouldn't
// want residual elements associated with this thread left on any lists.
guarantee(node.TState == ObjectWaiter::TS_RUN, "invariant");
assert(_owner == Self, "invariant");
assert(_succ != Self, "invariant");
} // OSThreadWaitState()
jt->set_current_waiting_monitor(NULL);
guarantee(_recursions == 0, "invariant");
_recursions = save; // restore the old recursion count
_waiters--; // decrement the number of waiters
// Verify a few postconditions
assert(_owner == Self, "invariant");
assert(_succ != Self, "invariant");
assert(((oop)(object()))->mark() == markOopDesc::encode(this), "invariant");
if (SyncFlags & 32) {
OrderAccess::fence();
}
// check if the notification happened
if (!WasNotified) {
// no, it could be timeout or Thread.interrupt() or both
// check for interrupt event, otherwise it is timeout
if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) {
TEVENT(Wait - throw IEX from epilog);
THROW(vmSymbols::java_lang_InterruptedException());
}
}
// NOTE: Spurious wake up will be consider as timeout.
// Monitor notify has precedence over thread interrupt.
}
总体来看,使用了timeout参数的wait()方法,会让线程等待设置的timeout时长后自动被唤醒,若在等待过程中线程执行了notify()或者notifyAll()则会被直接唤醒。我们看到上一小节的wait()方法是调用了wait(0),这里的timeout参数设置为0,则表示线程会永久等待直到notify()或notifyAll()将其唤醒。
源码:
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
这个方法比wait(long timeout)多了一个参数nanos,它是int类型,表示纳秒级别的附加时间,注释中说明它与timeout的共同作用时用了一个公式表示:1000000*timeout+nanos,这表明使用了这个方法后,线程处于等待状态的最大时间。但是看回代码,其实真实的计算应该是timeout++。