Maison > Java > javaDidacticiel > le corps du texte

Exemple d'analyse Java d'un objet du point de vue du code source du JDK

黄舟
Libérer: 2017-07-18 09:46:24
original
1472 Les gens l'ont consulté

Object est la classe parent de toutes les classes, ce qui signifie que toutes les classes en Java héritent directement ou indirectement de la classe Object. Par exemple, si vous créez une classe A avec désinvolture, même si cela n'est pas indiqué explicitement, la valeur par défaut est extends Object. Les trois points "..." après
indiquent qu'un nombre incertain de paramètres peut être accepté. L'ancienne façon d'écrire est Object args[], mais dans la nouvelle version de Java, il est recommandé d'utiliser ... pour l'exprimer. Par exemple,

public void getSomething(String ...  strings)(){}
Copier après la connexion

object est la classe parent de toutes les classes en Java, c'est-à-dire que toutes les classes, qu'elles soient créées par vous-même ou dans le système, héritent de la classe object, qui c'est-à-dire que toutes les classes La classe objet peut être remplacée à tout moment. Selon le principe de substitution de Liskov, une sous-classe peut remplacer sa classe parent à tout moment, mais la classe parent ne remplace pas nécessairement sa sous-classe. Java, c'est que tout est un objet. C'est la vérité ! La classe d'objet incarne les quatre caractéristiques majeures du polymorphisme, de l'héritage, de l'encapsulation et de l'abstraction dans la pensée oop !

La classe d'objets est la classe de base de toutes les classes, pas un type de données. Vous pouvez consulter la documentation jdk pour en savoir plus. Toutes les classes héritent d'Object.

Object ...objects Ce type de définition de paramètre est une expression polymorphe lorsque les paramètres de la méthode sont incertains. Autrement dit, cette méthode peut transmettre plusieurs paramètres et le nombre de paramètres est incertain. De cette façon, vous devez effectuer un traitement en conséquence dans le corps de la méthode. Étant donné que Object est la classe de base, le formulaire de paramètre Object...objects est utilisé pour permettre à tous les objets hérités de Object d'être utilisés comme paramètres. Cette méthode devrait être relativement rarement utilisée en pratique.

Object[] obj est un formulaire de paramètres composé d'un tableau Object. Notez que les paramètres de cette méthode sont fixes et constituent un tableau Object. Quant aux éléments stockés dans ce tableau, ils peuvent être des objets de toutes les classes qui héritent de Object.
Il est recommandé de lire plusieurs fois « Penser en Java » pour ces éléments de base

L'objet de Java est la classe parent de toutes les autres classes. Du point de vue de l'héritage, c'est la racine de niveau supérieur. , c'est donc aussi la seule classe A sans classe parent. Il contient certaines méthodes d'objets couramment utilisées, telles que getClass, hashCode, equals, clone, toString, notify, wait et d'autres méthodes courantes. Par conséquent, les autres classes n’ont pas besoin d’implémenter ces méthodes à plusieurs reprises après avoir hérité d’Object. La plupart de ces méthodes sont des méthodes natives, qui sont analysées en détail ci-dessous.

Le code principal est le suivant :

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 {}
}
Copier après la connexion

MéthoderegisterNatives

La méthode registerNatives étant modifiée par un bloc statique, cette méthode sera exécutée lorsque la classe Object est chargé. La méthode locale correspondante est Java_java_lang_Object_registerNatives, comme indiqué ci-dessous,

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

peut être vue qu'elle appelle indirectement la méthode de la structure JNINativeInterface_. Elle peut être simplement vue comme ceci : ce qu'il fait est probablement de convertir la couche Java. Les noms de méthodes correspondants en fonctions locales permettent au moteur d'exécution d'appeler des fonctions C/C++ basées sur ces tables de correspondance lors de l'exécution du bytecode. Comme indiqué ci-dessous, enregistrez ces méthodes et le moteur d'exécution peut transmettre le bytecode. relation lors de l'exécution de la méthode hashCode. Tableau pour trouver la fonction JVM_IHashCode de la JVM, où ()I peut également savoir que le type sur la couche Java doit être converti en type int. Ce mappage peut en fait être considéré comme un mappage de chaînes vers des pointeurs de fonction.

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},
};
Copier après la connexion

méthode getClass

la méthode getClass est également une méthode locale, et la méthode locale correspondante est Java_java_lang_Object_getClass, comme suit :

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);
    }
}
Copier après la connexion

Donc la méthode principale la chose ici est de voir la fonction GetObjectClass, la classe dans la couche Java correspond à klassOop dans la couche C++, donc les métadonnées et les informations de méthode sur la classe peuvent être obtenues via elle.

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
Copier après la connexion

Méthode hashCode

Il ressort de la méthode précédente registerNatives pour enregistrer plusieurs méthodes locales que la fonction correspondant à la méthode hashCode est JVM_IHashCode, c'est-à-dire

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
Copier après la connexion

pour La logique de génération du hashcode est déterminée par la fonction synchronizer.cpp de get_next_hash, et la mise en œuvre est relativement compliquée selon différentes valeurs du hashcode, et enfin un hachage. le masque est utilisé pour le traitement.

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;
}
Copier après la connexion

méthode égale

Il s'agit d'une méthode non locale, et la logique de jugement est également très simple, comparaison directe ==.

méthode clone

D'après le tableau des méthodes locales, nous savons que la fonction locale correspondant à la méthode clone est JVM_Clone La méthode clone implémente principalement la fonction de clonage de l'objet et génère un. nouvel objet identique basé sur l'objet (nous Si les propriétés des objets de classe commune sont de types primitifs, la valeur sera clonée, mais s'il s'agit d'objets, l'adresse de l'objet sera clonée). Pour implémenter le clonage, une classe Java doit implémenter l'interface Cloneable if (!klass->is_cloneable()) Ici, il sera vérifié si l'interface est implémentée. Déterminez ensuite s'il s'agit d'un tableau et allouez de l'espace mémoire dans deux cas. Le nouvel objet est new_obj, puis copiez et définissez la structure de données de la couche C++ de new_obj. Enfin, il est converti en type jobject pour faciliter la conversion vers le type Object de la couche Java.

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
Copier après la connexion

Méthode toString

La logique est d'obtenir le nom de la classe plus @ plus le hashCode hexadécimal.

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
Copier après la connexion

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() ;
  }
}
Copier après la connexion

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
Copier après la connexion

finalize方法

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal