JAVA程序实例:从JDK源码角度看Object
小职 2017-11-28 来源 :网络 阅读 629 评论 0

摘要:讲解JAVA程序实例之从JDK源码角度看Object。Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类。

讲解JAVA程序实例之从JDK源码角度看Object。Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类。它包含了对象常用的一些方法,比如getClass、hashCode、equals、clone、toString、notify、wait等常用方法。所以其他类继承了Object后就可以不用重复实现这些方法。这些方法大多数是native方法,下面具体分析。

主要的代码如下:

public class Object {

 

  private static native void registerNatives();

 

  static {

    registerNatives();

  }

 

  public final native Class<?> getClass();

 

  public native int hashCode();

 

  public boolean equals(Object obj) {

    return (this == obj);

  }

 

  protected native Object clone() throws CloneNotSupportedException;

 

  public String toString() {

    return getClass().getName() + "@" + Integer.toHexString(hashCode());

  }

 

  public final native void notify();

 

  public final native void notifyAll();

 

  public final native void wait(long timeout) throws InterruptedException;

 

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

  }

 

  public final void wait() throws InterruptedException {

    wait(0);

  }

 

  protected void finalize() throws Throwable {}

}

registerNatives方法

由于registerNatives方法被static块修饰,所以在加载Object类时就会执行该方法,对应的本地方法为Java_java_lang_Object_registerNatives,如下,

JNIEXPORT void JNICALLJava_java_lang_Object_registerNatives(JNIEnv *env, jclass cls){

    (*env)->RegisterNatives(env, cls,

                            methods, sizeof(methods)/sizeof(methods[0]));

}

可以看到它间接调用了JNINativeInterface_结构体的方法,简单可以看成是这样:它干的事大概就是将Java层的方法名和本地函数对应起来,方便执行引擎在执行字节码时根据这些对应关系表来调用C/C++函数,如下面,将这些方法进行注册,执行引擎执行到hashCode方法时就可以通过关系表来查找到JVM的JVM_IHashCode函数,其中()I还可以得知Java层上的类型应该转为int类型。这个映射其实就可以看成将字符串映射到函数指针。

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},

};

getClass方法

getClass方法也是个本地方法,对应的本地方法为Java_java_lang_Object_getClass,如下:

JNIEXPORT jclass JNICALLJava_java_lang_Object_getClass(JNIEnv *env, jobject this){

    if (this == NULL) {

        JNU_ThrowNullPointerException(env, NULL);

        return 0;

    } else {

        return (*env)->GetObjectClass(env, this);

    }

}

所以这里主要就是看GetObjectClass函数了,Java层的Class在C++层与之对应的则是klassOop,所以关于类的元数据和方法信息可以通过它获得。

JNI_ENTRY(jclass, jni_GetObjectClass(JNIEnv *env, jobject obj))

  JNIWrapper("GetObjectClass");

  DTRACE_PROBE2(hotspot_jni, GetObjectClass__entry, env, obj);

  klassOop k = JNIHandles::resolve_non_null(obj)->klass();

  jclass ret =

    (jclass) JNIHandles::make_local(env, Klass::cast(k)->java_mirror());

  DTRACE_PROBE1(hotspot_jni, GetObjectClass__return, ret);

  return ret;

JNI_END

hashCode方法

由前面registerNatives方法将几个本地方法注册可知,hashCode方法对应的函数为JVM_IHashCode,即

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生成的逻辑由synchronizer.cpp的get_next_hash函数决定,实现比较复杂,根据hashcode的不同值有不同的生成策略,最后使用一个hash掩码处理。

static inline intptr_t get_next_hash(Thread * Self, oop obj) {

  intptr_t value = 0 ;

  if (hashCode == 0) {

     value = os::random() ;

  } else

  if (hashCode == 1) {

     intptr_t addrBits = intptr_t(obj) >> 3 ;

     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;

  } else

  if (hashCode == 2) {

     value = 1 ;            // for sensitivity testing

  } else

  if (hashCode == 3) {

     value = ++GVars.hcSequence ;

  } else

  if (hashCode == 4) {

     value = intptr_t(obj) ;

  } else {

     unsigned t = Self->_hashStateX ;

     t ^= (t << 11) ;

     Self->_hashStateX = Self->_hashStateY ;

     Self->_hashStateY = Self->_hashStateZ ;

     Self->_hashStateZ = Self->_hashStateW ;

     unsigned v = Self->_hashStateW ;

     v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;

     Self->_hashStateW = v ;

     value = v ;

  }

 

  value &= markOopDesc::hash_mask;

  if (value == 0) value = 0xBAD ;

  assert (value != markOopDesc::no_hash, "invariant") ;

  TEVENT (hashCode: GENERATE) ;

  return value;

}

equals方法

这是一个非本地方法,判断逻辑也十分简单,直接==比较。

clone方法

由本地方法表知道clone方法对应的本地函数为JVM_Clone,clone方法主要实现对象的克隆功能,根据该对象生成一个相同的新对象(我们常见的类的对象的属性如果是原始类型则会克隆值,但如果是对象则会克隆对象的地址)。Java的类要实现克隆则需要实现Cloneable接口,if (!klass->is_cloneable())这里会校验是否有实现该接口。然后判断是否是数组分两种情况分配内存空间,新对象为new_obj,接着对new_obj进行copy及C++层数据结构的设置。最后再转成jobject类型方便转成Java层的Object类型。

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;

 

  if (!klass->is_cloneable()) {

    ResourceMark rm(THREAD);

    THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name());

  }

 

  const int size = obj->size();

  oop new_obj = NULL;

  if (obj->is_javaArray()) {

    const int length = ((arrayOop)obj())->length();

    new_obj = CollectedHeap::array_allocate(klass, size, length, CHECK_NULL);

  } else {

    new_obj = CollectedHeap::obj_allocate(klass, size, CHECK_NULL);

  }

  Copy::conjoint_jlongs_atomic((jlong*)obj(), (jlong*)new_obj,

                               (size_t)align_object_size(size) / HeapWordsPerLong);

  new_obj->init_mark();

 

  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, size));

 

  if (klass->has_finalizer()) {

    assert(obj->is_instance(), "should be instanceOop");

    new_obj = instanceKlass::register_finalizer(instanceOop(new_obj), CHECK_NULL);

  }

 

  return JNIHandles::make_local(env, oop(new_obj));

JVM_END

toString方法

逻辑是获取class名称加上@再加上十六进制的hashCode。

notify方法

此方法用来唤醒线程,final修饰说明不可重写。与之对应的本地方法为JVM_MonitorNotify,ObjectSynchronizer::notify最终会调用ObjectMonitor::notify(TRAPS),这个过程是ObjectSynchronizer会尝试当前线程获取free ObjectMonitor对象,不成功则尝试从全局中获取。

JVM_ENTRY(void, JVM_MonitorNotify(JNIEnv* env, jobject handle))

  JVMWrapper("JVM_MonitorNotify");

  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));

  assert(obj->is_instance() || obj->is_array(), "JVM_MonitorNotify must apply to an object");

  ObjectSynchronizer::notify(obj, CHECK);

JVM_END

ObjectMonitor对象包含一个_WaitSet队列对象,此对象保存着所有处于wait状态的线程,用ObjectWaiter对象表示。notify要做的事是先获取_WaitSet队列锁,再取出_WaitSet队列中第一个ObjectWaiter对象,再根据不同策略处理该对象,比如把它加入到_EntryList队列中。然后再释放_WaitSet队列锁。它并没有释放synchronized对应的锁,所以锁只能等到synchronized同步块结束时才释放。

void ObjectMonitor::notify(TRAPS) {

  CHECK_OWNER();

  if (_WaitSet == NULL) {

     TEVENT (Empty-Notify) ;

     return ;

  }

  DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);

 

  int Policy = Knob_MoveNotifyee ;

 

  Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ;

  ObjectWaiter * iterator = DequeueWaiter() ;

  if (iterator != NULL) {

     TEVENT (Notify1 - Transfer) ;

     guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ;

     guarantee (iterator->_notified == 0, "invariant") ;

     if (Policy != 4) {

        iterator->TState = ObjectWaiter::TS_ENTER ;

     }

     iterator->_notified = 1 ;

 

     ObjectWaiter * List = _EntryList ;

     if (List != NULL) {

        assert (List->_prev == NULL, "invariant") ;

        assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;

        assert (List != iterator, "invariant") ;

     }

 

     if (Policy == 0) {       // prepend to EntryList

         if (List == NULL) {

             iterator->_next = iterator->_prev = NULL ;

             _EntryList = iterator ;

         } else {

             List->_prev = iterator ;

             iterator->_next = List ;

             iterator->_prev = NULL ;

             _EntryList = iterator ;

        }

     } else

     if (Policy == 1) {      // append to EntryList

         if (List == NULL) {

             iterator->_next = iterator->_prev = NULL ;

             _EntryList = iterator ;

         } else {

            // CONSIDER:  finding the tail currently requires a linear-time walk of

            // the EntryList.  We can make tail access constant-time by converting to

            // a CDLL instead of using our current DLL.

            ObjectWaiter * Tail ;

            for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;

            assert (Tail != NULL && Tail->_next == NULL, "invariant") ;

            Tail->_next = iterator ;

            iterator->_prev = Tail ;

            iterator->_next = NULL ;

        }

     } else

     if (Policy == 2) {      // prepend to cxq

         // prepend to cxq

         if (List == NULL) {

             iterator->_next = iterator->_prev = NULL ;

             _EntryList = iterator ;

         } else {

            iterator->TState = ObjectWaiter::TS_CXQ ;

            for (;;) {

                ObjectWaiter * Front = _cxq ;

                iterator->_next = Front ;

                if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {

                    break ;

                }

            }

         }

     } else

     if (Policy == 3) {      // append to cxq

        iterator->TState = ObjectWaiter::TS_CXQ ;

        for (;;) {

            ObjectWaiter * Tail ;

            Tail = _cxq ;

            if (Tail == NULL) {

                iterator->_next = NULL ;

                if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) {

                   break ;

                }

            } else {

                while (Tail->_next != NULL) Tail = Tail->_next ;

                Tail->_next = iterator ;

                iterator->_prev = Tail ;

                iterator->_next = NULL ;

                break ;

            }

        }

     } else {

        ParkEvent * ev = iterator->_event ;

        iterator->TState = ObjectWaiter::TS_RUN ;

        OrderAccess::fence() ;

        ev->unpark() ;

     }

 

     if (Policy < 4) {

       iterator->wait_reenter_begin(this);

     }

 

     // _WaitSetLock protects the wait queue, not the EntryList.  We could

     // move the add-to-EntryList operation, above, outside the critical section

     // protected by _WaitSetLock.  In practice that's not useful.  With the

     // exception of  wait() timeouts and interrupts the monitor owner

     // is the only thread that grabs _WaitSetLock.  There's almost no contention

     // on _WaitSetLock so it's not profitable to reduce the length of the

     // critical section.

  }

 

  Thread::SpinRelease (&_WaitSetLock) ;

 

  if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {

     ObjectMonitor::_sync_Notifications->inc() ;

  }

}

notifyAll方法

与notify方法类似,只是在取_WaitSet队列时不是取第一个而是取所有。

wait方法

wait方法是让线程等待,它对应的本地方法是JVM_MonitorWait,间接调用了ObjectSynchronizer::wait,与notify对应,它也是对应调用ObjectMonitor对象的wait方法。该方法较长,这里不贴出来了,大概就是创建一个ObjectWaiter对象,接着获取_WaitSet队列锁将ObjectWaiter对象添加到该队列中,再释放队列锁。另外,它还会释放synchronized对应的锁,所以锁没有等到synchronized同步块结束时才释放。

JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))

  JVMWrapper("JVM_MonitorWait");

  Handle obj(THREAD, JNIHandles::resolve_non_null(handle));

  assert(obj->is_instance() || obj->is_array(), "JVM_MonitorWait must apply to an object");

  JavaThreadInObjectWaitState jtiows(thread, ms != 0);

  if (JvmtiExport::should_post_monitor_wait()) {

    JvmtiExport::post_monitor_wait((JavaThread *)THREAD, (oop)obj(), ms);

  }

  ObjectSynchronizer::wait(obj, ms, CHECK);

JVM_END

finalize方法

这个方法用于当对象被回收时调用,这个由JVM支持,Object的finalize方法默认是什么都没有做,如果子类需要在对象被回收时执行一些逻辑处理,则可以重写finalize方法。


希望这篇文章可以帮助到你。总之,同学们,你想要的职坐标IT频道都能找到!

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程