Heim  >  Artikel  >  Java  >  Java-Beispielanalyse von Object aus der Perspektive des JDK-Quellcodes

Java-Beispielanalyse von Object aus der Perspektive des JDK-Quellcodes

黄舟
黄舟Original
2017-07-18 09:46:241416Durchsuche

Object ist die übergeordnete Klasse aller Klassen, was bedeutet, dass alle Klassen in Java direkt oder indirekt von der Object-Klasse erben. Wenn Sie beispielsweise beiläufig eine Klasse A erstellen, obwohl dies nicht explizit angegeben ist, wird standardmäßig „extends Object“ verwendet. Die drei Punkte „…“ nach
zeigen an, dass eine unbestimmte Anzahl von Parametern akzeptiert werden kann. Die alte Schreibweise ist Object args [], aber in der neuen Version von Java wird empfohlen, ... zu verwenden, um es auszudrücken. Beispielsweise ist

public void getSomething(String ...  strings)(){}

object die übergeordnete Klasse aller Klassen in Java, was bedeutet, dass alle Klassen, unabhängig davon, ob sie von Ihnen selbst oder im System erstellt wurden, von der Objektklasse erben, also alle Klassen Die Objektklasse kann bei jeder Gelegenheit ersetzt werden. Nach dem Liskov-Substitutionsprinzip kann eine Unterklasse ihre übergeordnete Klasse jederzeit ersetzen, aber die übergeordnete Klasse ersetzt möglicherweise nicht unbedingt ihre Unterklasse. In Java wird oft gesagt, dass dies der Fall ist ein Objekt. Das ist die Wahrheit! Die Objektklasse verkörpert die vier Hauptmerkmale Polymorphismus, Vererbung, Kapselung und Abstraktion im Oop-Denken!

Die Objektklasse ist die Basisklasse aller Klassen, kein Datentyp. Weitere Informationen hierzu finden Sie in der JDK-Dokumentation. Alle Klassen erben von Object.

Objekt ...Objekte Diese Art der Parameterdefinition ist ein polymorpher Ausdruck, wenn die Methodenparameter unsicher sind. Das heißt, diese Methode kann mehrere Parameter übergeben und die Anzahl der Parameter ist ungewiss. Auf diese Weise müssen Sie im Methodenkörper eine entsprechende Verarbeitung durchführen. Da Object die Basisklasse ist, wird die Parameterform Object...objects verwendet, um die Verwendung aller von Object geerbten Objekte als Parameter zu ermöglichen. Diese Methode sollte in der Praxis relativ selten angewendet werden.

Object[] obj ist eine Parameterform, die aus einem Object-Array besteht. Beachten Sie, dass die Parameter dieser Methode fest sind und ein Object-Array sind. Die in diesem Array gespeicherten Elemente können Objekte aller Klassen sein, die von Object erben.
Es wird empfohlen, dass Sie für diese grundlegenden Dinge mehrmals „Think in Java“ lesen

Java-Objekt ist die übergeordnete Klasse aller anderen Klassen. Aus Sicht der Vererbung ist es die Wurzel der obersten Ebene Es handelt sich also auch um die einzige A-Klasse ohne übergeordnete Klasse. Es enthält einige häufig verwendete Methoden von Objekten, wie z. B. getClass, hashCode, equals, clone, toString, notify, wait und andere gängige Methoden. Daher müssen andere Klassen diese Methoden nach dem Erben von Object nicht wiederholt implementieren. Bei den meisten dieser Methoden handelt es sich um native Methoden, die im Folgenden detailliert analysiert werden.

Der Hauptcode lautet wie folgt:

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-Methode

Da die registerNatives-Methode durch einen statischen Block geändert wird, wird diese Methode ausgeführt, wenn die Object-Klasse Die entsprechende lokale Methode ist Java_java_lang_Object_registerNatives, wie unten gezeigt,

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
}

kann gesehen werden, dass sie indirekt die Methode der JNINativeInterface_-Struktur aufruft Es kann einfach so gesehen werden. Was es bewirkt, ist wahrscheinlich die Konvertierung der entsprechenden Methodennamen in lokale Funktionen der Java-Schicht. Dies erleichtert der Ausführungs-Engine das Aufrufen von C/C++-Funktionen basierend auf diesen Korrespondenztabellen, wenn sie Bytecode ausführt. Wie unten gezeigt, können diese Methoden registriert werden, und die Ausführungs-Engine kann sie übergeben Beziehung beim Ausführen der hashCode-Methode, um die JVM_IHashCode-Funktion von JVM zu finden, wobei ()I auch wissen kann, dass der Typ auf der Java-Ebene in den Typ int konvertiert werden soll. Diese Zuordnung kann tatsächlich als Zuordnung von Zeichenfolgen zu Funktionszeigern angesehen werden.

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-Methode

getClass-Methode ist auch eine lokale Methode, und die entsprechende lokale Methode ist Java_java_lang_Object_getClass, wie folgt:

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

Also die Hauptsache Was hier zu sehen ist, ist die GetObjectClass-Funktion, die Klasse in der Java-Schicht entspricht klassOop in der C++-Schicht, sodass Metadaten und Methodeninformationen über die Klasse über sie abgerufen werden können.

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-Methode

Aus der vorherigen registerNatives-Methode zum Registrieren mehrerer lokaler Methoden geht hervor, dass die der hashCode-Methode entsprechende Funktion JVM_IHashCode ist, also

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

für Die Logik der Hashcode-Generierung wird durch die synchronizer.cpp-Funktion von get_next_hash bestimmt, und die Implementierung ist relativ kompliziert. Es gibt verschiedene Generierungsstrategien entsprechend unterschiedlichen Hashcode-Werten und schließlich einem Hash Für die Verarbeitung wird eine Maske verwendet.

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

Gleich-Methode

Dies ist eine nicht lokale Methode, und die Beurteilungslogik ist ebenfalls sehr einfach, direkter == Vergleich.

Klonmethode

Aus der lokalen Methodentabelle wissen wir, dass die der Klonmethode entsprechende lokale Funktion JVM_Clone ist. Die Klonmethode implementiert hauptsächlich die Klonfunktion des Objekts und generiert eine Identisches neues Objekt basierend auf dem Objekt (wir Wenn die Eigenschaften gemeinsamer Klassenobjekte primitive Typen sind, wird der Wert geklont, wenn es sich jedoch um Objekte handelt, wird die Adresse des Objekts geklont). Um das Klonen zu implementieren, muss eine Java-Klasse die Cloneable-Schnittstelle implementieren. if (!klass->is_cloneable()) Hier wird überprüft, ob die Schnittstelle implementiert ist. Bestimmen Sie dann, ob es sich um ein Array handelt, und weisen Sie in zwei Fällen Speicherplatz zu. Das neue Objekt ist new_obj und kopiert und legt dann die C++-Schichtdatenstruktur von new_obj fest. Schließlich wird es in den Jobject-Typ konvertiert, um die Konvertierung in den Object-Typ der Java-Schicht zu erleichtern.

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-Methode

Die Logik besteht darin, den Klassennamen plus @ plus den hexadezimalen HashCode zu erhalten.

notify方法

此方法用来唤醒线程,final修饰说明不可重写。与之对应的本地方法为JVM_MonitorNotifyObjectSynchronizer::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&#39;s not useful.  With the
     // exception of  wait() timeouts and interrupts the monitor owner
     // is the only thread that grabs _WaitSetLock.  There&#39;s almost no contention
     // on _WaitSetLock so it&#39;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方法。

Das obige ist der detaillierte Inhalt vonJava-Beispielanalyse von Object aus der Perspektive des JDK-Quellcodes. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn