先有Class還是先有Object?

簡短答案:“雞・蛋”問題通常都是通過一種叫“自舉”(bootstrap)的過程來解決的。

其實“雞蛋問題”的根本矛盾就在於假定了“雞”或“蛋”的其中一個要先進入“完全可用”的狀態。而許多現實中被簡化爲“雞蛋問題”的情況實際可以在“混沌”中把“雞”和“蛋”都初始化好,而不存在先後問題;在它們初始化的過程中,兩者都不處於“完全可用”狀態,而完成初始化後它們就同時都進入了可用狀態。

打個比方,番茄炒蛋。並不是要先把番茄完全炒好,然後把雞蛋完全炒好,然後把它們混起來;而是先炒番茄炒到半熟,再炒雞蛋炒到半熟,然後把兩個半熟的部分混在一起同時炒熟。

引用題主的問題:
Java的對象模型中:
  1. 所有的類都是Class類的實例,Object是類,那麼Object也是Class類的一個實例。
  2. 所有的類都最終繼承自Object類,Class是類,那麼Class也繼承自Object。
這個問題中,第1個假設是錯的:java.lang.Object是一個Java類,但並不是java.lang.Class的一個實例。後者只是一個用於描述Java類與接口的、用於支持反射操作的類型。這點上Java跟其它一些更純粹的面嚮對象語言(例如Python和Ruby)不同。
而第2個假設是對的:java.lang.Class是java.lang.Object的派生類,前者繼承自後者。

雖然第1個假設不對,但“雞蛋問題”仍然存在:在一個已經啓動完畢、可以使用的Java對象系統裏,必須要有一個java.lang.Class實例對應java.lang.Object這個類;而java.lang.Class是java.lang.Object的派生類,按“一般思維”前者應該要在後者完成初始化之後纔可以初始化…

事實是:這些相互依賴的核心類型完全可以在“混沌”中一口氣都初始化好,然後對象系統的狀態才叫做完成了“bootstrap”,後面就可以按照Java對象系統的一般規則去運行。JVM、JavaScript、Python、Ruby等的運行時都有這樣的bootstrap過程。

在“混沌”(boostrap過程)裏,
  • JVM可以爲對象系統中最重要的一些核心類型先分配好內存空間,讓它們進入[已分配空間]但[尚未完全初始化]狀態。此時這些對象雖然已經分配了空間,但因爲狀態還不完整所以尚不可使用。
  • 然後,通過這些分配好的空間把這些核心類型之間的引用關係串好。到此爲止所有動作都由JVM完成,尚未執行任何Java字節碼。
  • 然後這些核心類型就進入了[完全初始化]狀態,對象系統就可以開始自我運行下去,也就是可以開始執行Java字節碼來進一步完成Java系統的初始化了。

在HotSpot VM裏,有一個叫做“Universe”的C++類用於記錄對象系統的總體狀態。它有這麼兩個有趣的字段記錄當前是處於bootstrapping階段還是已經完全初始化好:
jdk8u/jdk8u/hotspot: ade5be2b1758 src/share/vm/memory/universe.hpp
  static bool is_bootstrapping()                      { return _bootstrapping; }
  static bool is_fully_initialized()                  { return _fully_initialized; }
然後Universe::genesis()函數會在bootstrap階段中創建核心類型的對象模型:
jdk8u/jdk8u/hotspot: ade5be2b1758 src/share/vm/memory/universe.cpp
(“genesis”是創世紀的意思,多麼形象)
其中會調用SystemDictionary::initialize()來初始化對象系統的核心類型:
jdk8u/jdk8u/hotspot: ade5be2b1758 src/share/vm/classfile/systemDictionary.cpp
其中會進一步跑到SystemDictionary::initialize_preloaded_classes()來創建java.lang.Object、java.lang.Class等核心類型:
jdk8u/jdk8u/hotspot: ade5be2b1758 src/share/vm/classfile/systemDictionary.cpp
這個函數在加載了java.lang.Object、java.lang.Class等核心類型後會調用Universe::fixup_mirrors()來完成前面說的“把引用關係串起來”的動作:
  // Fixup mirrors for classes loaded before java.lang.Class.
  // These calls iterate over the objects currently in the perm gen
  // so calling them at this point is matters (not before when there
  // are fewer objects and not later after there are more objects
  // in the perm gen.
  Universe::initialize_basic_type_mirrors(CHECK);
  Universe::fixup_mirrors(CHECK);
jdk8u/jdk8u/hotspot: ade5be2b1758 src/share/vm/memory/universe.cpp
void Universe::fixup_mirrors(TRAPS) {
  // Bootstrap problem: all classes gets a mirror (java.lang.Class instance) assigned eagerly,
  // but we cannot do that for classes created before java.lang.Class is loaded. Here we simply
  // walk over permanent objects created so far (mostly classes) and fixup their mirrors. Note
  // that the number of objects allocated at this point is very small.

  // ...
}
就是這樣。

=======================================================

Python裏的對象系統裏也有這種“雞蛋問題”,也是通過一個bootstrap過程來解決。
“雞蛋問題”在於:Python裏的所有類型都確實用一個type object表示,而所有類型都是object類的子類。
換句話說,<type 'type'>類是<type 'object'>的子類;而<type 'object'>既是類又是個對象,是<type 'type'>的實例。這個情況就跟題主原本所想像的Java裏的情況一樣——雖然Java並非如此。
關於CPython 2.5的對象系統初始化的剖析,可以參考《Python源碼剖析》的第12章。
具體到CPython 2.7.x的代碼,pythonrun.c的Py_InitializeEx()會做Python運行時的初始化,其中會調用object.c的_Py_ReadyTypes()來按照一個列表的順序初始化核心類型的type對象,具體的初始化動作在typeobject.c的PyType_Ready()

這些核心類型的type對象在CPython裏的C層面的類型是PyTypeObject,其結構是確定的;它們的存儲空間通過靜態變量分配,例如<type 'type'>就聲明爲在object.h的PyTypeObject PyType_Type,對應的還有<type 'object'>的PyTypeObject PyBaseObject_Type。
所以在進行初始化動作之前它們的存儲空間就已經有着落了,真正做初始化時只要把它們的相互引用串起來就好。

=======================================================

Ruby裏的雞蛋問題跟Python比較相似。Ruby裏的所有類都是Class類的實例,而Class類是Object類的子類。

以CRuby 2.2.1爲例,核心類型的初始化在這裏:class.c的Init_class_hierarchy(),可以看到也是典型的bootstrap過程:先分配空間,再把相互引用關係串起來,然後完成bootstrap開始進入正常的對象系統運作。

void
Init_class_hierarchy(void)
{
    /* 給核心類型的Class對象實例分配空間並串上它們的繼承關係 */
    rb_cBasicObject = boot_defclass("BasicObject", 0);
    rb_cObject = boot_defclass("Object", rb_cBasicObject);
    rb_cModule = boot_defclass("Module", rb_cObject);
    rb_cClass =  boot_defclass("Class",  rb_cModule);

    rb_const_set(rb_cObject, rb_intern_const("BasicObject"), rb_cBasicObject);

    /* 讓上面創建的Class對象實例的類型信息(klass字段)指向Class對象 */
    RBASIC_SET_CLASS(rb_cClass, rb_cClass);
    RBASIC_SET_CLASS(rb_cModule, rb_cClass);
    RBASIC_SET_CLASS(rb_cObject, rb_cClass);
    RBASIC_SET_CLASS(rb_cBasicObject, rb_cClass);
}
然後看調用它的上層函數:
/*!
 * Initializes the world of objects and classes.
 *
 * At first, the function bootstraps the class hierarchy.
 * It initializes the most fundamental classes and their metaclasses.
 * - \c BasicObject
 * - \c Object
 * - \c Module
 * - \c Class
 * After the bootstrap step, the class hierarchy becomes as the following
 * diagram.
 *
 * \image html boottime-classes.png
 *
 * Then, the function defines classes, modules and methods as usual.
 * \ingroup class
 */

/* ... */
void
Init_Object(void)
{
    Init_class_hierarchy();

    /* ... */
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章