基于JDK9从JVM源码研究类java.lang.Object

  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如下:

  • Object()
  • registerNatives()
  • clone()
  • equals(Object obj)
  • finalize()
  • getClass()
  • hashCode()
  • notify()
  • notifyAll()
  • toString()
  • wait()
  • wait(long timeout)
  • wait(long timeout, int nanos)

  接下来我们一个个地进行解读。

1、 Object()

  Java中规定:在类定义过程中,对于未定义构造函数的类,默认会有一个无参数的构造函数,Object类作为所有类的基类,当然也会有自己的构造方法。其实在jdk9之前的版本中,这个构造函数都是没有显式写出来的,而我们可以看到从这个版本开始,构造函数被显式写出。

  源码:

@HotSpotIntrinsicCandidate
public Object() {}

2、 registerNatives()

  源码:

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 <stdio.h>
#include <signal.h>
#include <limits.h>

#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方法,这个是后话,不在本篇讨论。

3、 clone()

  源码:

@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是浅拷贝,关于浅拷贝和深拷贝这里就先不进行讨论了。

4、 equals(Object obj)

  源码:

public boolean equals(Object obj) {
        return (this == obj);
}

  equals()方法用于比较其他对象与本对象是否等同,它适用于非空对象之间的对比。从它的注释来看,它拥有以下这几个特性(自反性、对称性、传递性、一致性、非空性):

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

  从代码中我们可以看到,默认的equals()实现采用的是等价符,也就是说如果不重写该方法,则equals()与“==”是等效的,所以一般在需要时,我们会重写类的equals()方法,但一定要谨记要遵守它的5个特性。另外,如果是基本数据类型之间的比较,“==”比较的是数值,而如果是复合数据类型之间的比较,则它比较的是对象的引用地址是否相等。

5、 finalize()

  源码:

@Deprecated(since="9")
protected void finalize() throws Throwable { }

  finalize()方法在对象终结时调用,它在GC回收对象时会自动被调用,但JVM不保证finalize()方法一定会被调用,也就是说它的自动调用是不确定的。当然,基于这个原因,当初SUN就不提倡大家使用这个方法。现在我们看到再JDK9中,这个方法终于被标记为Deprecated,即为过时方法,从注释中也可以看出,Oracle建议用java.lang.ref.Cleaner来替代finalize()的使用。

6、 getClass()

  源码:

@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()是一个类的实例所具备的方法,它是在运行时才确定的,所以若此时实例终止了,则会抛出空指针异常。

7、 hashCode()

  源码:

@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类型的数值。

8、 notify()

  源码:

@HotSpotIntrinsicCandidate
public final native void notify();

  这个方法用来唤醒一个在当前对象监视器(monitor)里的正等待唤醒的线程,而且notify()只能在本身拥有对应对象监视器的对象上去调用,获得对象本身的监视器有三种途径:

  • By executing a synchronized instance method of that object.
  • By executing the body of a synchronized statement that synchronizes on the object.
  • For objects of type Class, by executing a synchronized static method of that class.

  也就是从执行该对象的同步方法中获取、从执行该对象的同步块中获取、从执行类的静态同步方法中获取。

  从源码分析,我们依然还是在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));
}

  如此,线程被唤醒。

9、 notifyAll()

  源码:

@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));
}

10、 toString()

  源码:

public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

  toString()方法返回的是一串用文本形式来描述这个对象的字符串,它应该尽量简洁但也要能体现丰富的信息,便于阅读。注释中建议我们在所有子类中重写此方法。

  从代码中可以看到,Object类的toString()方法返回的是该类的对象的实例名和用16进制表示的该对象的地址(哈希码),中间用“@”隔开。

11、 wait()

  源码:

public final void wait() throws InterruptedException {
    wait(0);
}

  wait()方法用来让当前线程处于等待状态,直到线程被notify()或者notifyAll()唤醒。不带参数的wait()相当于执行了wait(0)。

12、 wait(long timeout)

  源码:

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()将其唤醒。

13、 wait(long timeout, int nanos)

  源码:

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++。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值