Object类是如何实现的(例如hashCode方法和内部字段)?

9
我很好奇Object类是如何实现的。例如:
  1. 一个方法hashCode()wait()
  2. 内部状态是如何表示的。
    例如,内在锁定或存储调用对象的wait()的线程的数据结构。
为了找到这些信息,我下载了OpenJDK的源代码并开始挖掘。首先遇到的是\openjdksrc\jdk\src\share\native\java\lang\Object.c文件,其中包括:
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);
    }
}

根据我的理解,methods[]数组定义了Object方法本地实现之间的映射关系。例如,Object的hashCode()被映射到JVM_IHashCode函数。JVM_IHashCode在\openjdksrc\hotspot\src\share\vm\prims\jvm.cpp中被实现。这里是我的第一个问题。为什么它已经成为VM本身的一部分(已经在\openjdksrc\hotspot\src\share\vm中定义了)? 但是让我们转向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

如果对象为null,为什么我们在此处返回0? 我想应该抛出NPE。否则,FastHashCode将从\ openjdksrc \ hotspot \ src \ share \ vm \ runtime \ synchronizer.cpp调用,并且最终在某个时间点上,将调用get_next_hash来计算实际值。一旦计算完成,问题是它存储在哪里?

    intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) {

...CUT...

      ObjectMonitor* monitor = NULL;
      markOop temp, test;
      intptr_t hash;
      markOop mark = ReadStableMark (obj);
    
...CUT...

      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.
      } 
...CUT...
      return hash;
    }

所以,oop类/结构体(?)有一个markOop类/结构体(?),其中存储了哈希值。有趣的是,我找不到这些类/结构体。我能找到的只有:
class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark;
...CUT...

在\ openjdksrc \ hotspot \ src \ share \ vm \ oops \ oop.hpp中,似乎在私有字段中具有markOop。但是在代码的其余部分中引用了什么“oop”? markOop定义在哪里? 我已经找到了一个对应的:
class markOopDesc: public oopDesc 
...CUT...

在\openjdksrc\hotspot\src\share\vm\oops\markOop.hpp中,但它只充满了枚举,找不到可以存储哈希值的字段。

如果有人能回答我问题的一部分,我将非常感激。谢谢!


1
null的hashCode确实应该是零:http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/System.html#identityHashCode(java.lang.Object) - int3
为什么不在Object类中使用静态计数器? - neoexpert
1个回答

2
一旦Java对象的哈希码被计算出来,它就会被存储在对象头中。以下是相关链接:http://www.javamex.com/tutorials/memory/object_memory_usage.shtmlhttp://hunmr.blogspot.com/2012/08/java-performance-tunning.html。此外,该信息也可以从 hotspot/src/share/vm/oops/markOop.hpp 中获取。请注意保留HTML标签。
// The markOop describes the header of an object.
//
// Note that the mark is not a real oop but just a word.
// It is placed in the oop hierarchy for historical reasons.
//
// Bit-format of an object header (most significant first, big endian layout below):
//
//  32 bits:
//  --------
//             hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
//             JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
//             size:32 ------------------------------------------>| (CMS free block)
//             PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//

1
参考:这里的“OOP”表示“普通对象指针”。 - Marko Topolnik
谢谢!我脑海中又冒出了几个问题。我在你提到的网站上读到对象头占用8个字节。但是这里看起来它占用了16个字节(每行4个字节*4行)。我也很好奇一些字段的大小。为什么哈希只有25位?锁为什么只有2位?我认为只需要1位锁就足够了。还有,年龄、时代和推广位是什么字段? - Janek
@Janek,好问题。我认为“正常”和“偏向”对象状态是互斥的。对于“CMD free”和“CMS promoted”也是如此。年龄用于实现基于代的内存管理。时期?促销位?我不知道。所有答案都在源代码中。 :) - whunmr
@whunmr 当你说哈希码存储在“对象头文件”中时,你是什么意思?头文件是C/C++中的静态文件吗?还是为每个类生成一个新的头文件,在其中一个值是你上面描述的32位数据? - Mathew Kurian
@mk1 对于“对象头”,我指的是Java对象实例占用的内存的头部/开头部分。 - whunmr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接