DCSIMG
Objects Structure: Under The Hood - Liran Chen's Blog

Liran Chen's Blog

.Net Internals, Debugging, Multithreading - and More!

Objects Structure: Under The Hood

אובייקטים בדוט-נט אינם בנויים בתור יחידות בודדת ושלמות שנמצאות על ה-GC Heap, שמכילות את ה-Data Member'ים שהגדרנו להן.
בפועל, מעבר למידע "הנראה לעין" שאנחנו מגדירים כשאנחנו כותבים מחלקה חדשה, מתווספים גם מספר שדות "בלתי נראים" לאובייקט שלנו שנמצאים באזורים שונים בזכרון.

השדה הראשון והבסיסי הוא המצביע ל-MethodTable השייך לטיפוס שלנו. אני מדלג על ההסבר על ה-MethodTable והמידע שהיא מכילה (שימו לב שלא מדובר ב-VTable עצמו, אלא בטיפוס שבין היתר מכיל מצביע אליה), ובמקום זה אתמקד בנקודה אחרת: מיקום המצביע.
העניין הוא כזה, כשאנחנו יוצרים מופע של מחלקה, יש לנו גישה אך ורק ל-Member'ים שהגדרנו. כלומר, שדות "נסתרים" שמתווספים אוטומטית לאובייקט שלנו נמצאים מעבר להישג ידנו. ועם זאת, עדיין חלקם מוסתרים "פחות" ומוסתרים "יותר". אני אסביר למה הכוונה..
מאחר ואנחנו נוגעים כאן בנושאים שנמצאים "בברזלים" של הפלטפורמה, מבט בקוד ה-IL של המחלקה Object למשל לא יעזור לנו מאחר ורוב המידע "המעניין" נסתר מאיתנו שם. במקום זה, נציץ בקוד של SSCLI על מנת לקבל הבנה קצת יותר טובה על מה שקורה מאחורי הקלעים.

קובץ ה-Header של Object נמצא תחת הכתובת clr/src/vm/object.h. כשנפתח אותו, נראה את הקוד הבא:

class Object

{

  protected:

    MethodTable*    m_pMethTab;

    // ... lots of code

}


ממה שאנחנו רואים כאן, אפשר להתרשם שלאובייקט שלנו מתווסף "שדה נסתר" אחד בלבד, שמצביע ל-VTable שמשוייך לטיפוס. אבל בפועל, המצב מעט שונה.
בנוסף ל-VPointer שהתווסף לנו לתוך תחום האובייקט שלנו, מתווסף לנו אוטומטית גם ה-ObjHeader. הסיבה שאנחנו לא רואים כל אזכור לו בהגדרה של מחלקת Object היא שהוא לא נמצא בתוכה, אלא ב-Offset שלילי (דהיינו, ממוקם לפניה בזכרון).
ה-ObjHeader בנוי מ-DWORD שמחזיק בתוכו מספר ערכים שונים, שכוללים בין היתר את אינדקס ה-SyncBlock שמשוייך אליו (יפורט בפוסט עתידי), מספר ה-AppDomain בו האובייקט קיים, ערך ה-Hashcode של האובייקט, וביט נוסף לשימושי SpinLock שנעשים על האובייקט.
מאחר ולכל ביט וביט יש משמעות שונה כאן, העבודה מול ה-ObjHeader נעשית על הביטים עצמם, דרך מניפולציות שונות של Bit Mask'ים כאלו ואחרים. אפשר לקחת בתור דוגמה את הפונקציה GetHeaderSyncBlockIndex שמיצאת את אינדקס ה-SyncBlock מתוך ה-ObjHeader:

// Access to the Sync Block Index, by masking the Value.

DWORD GetHeaderSyncBlockIndex()

{

    // pull the value out before checking it to avoid race condition

    DWORD value = m_SyncBlockValue;

    if ((value & (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE))

        != BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX)

    {

        return 0;

    }

 

    return value & MASK_SYNCBLOCKINDEX;

}


רק לשם ההבהרה, שימו לב שהקוד הזה אינו נמצא תחת מחלקת Object, אלא תחת המחלקה ObjHeader.
ההתיחסות היחידה ל-ObjHeader מתוך המחלקה Object נעשית כאשר רוצים לקבל את ה-ObjHeader, שמיוחס לאותו אובייקט. מדובר בפונקציית Getter פשוטה שפונה ל-Offset המתאים ומחזירה את אותו ObjHeader.

ObjHeader   *GetHeader()

{

    return PTR_ObjHeader(PTR_HOST_TO_TADDR(this) - sizeof(ObjHeader));

}


לסיכום,ניתן לייצג את מבנה האובייקט בזכרון בצורה הבאה:
 

תוכן התגובה

Shlomo כתב/ה:

מחכה בקוצר רוח לפוסט ההמשך

# June 5, 2009 7:02 PM

Beyond The Spec כתב/ה:

בדוט-נט קיים מנגנון מובנה לסנכרון קטעי קוד. ובין אם אנחנו מודעים לזה או לא, אנחנו משתמשים בו בכל פעם

# June 12, 2009 12:05 PM

Beyond The Spec כתב/ה:

WinDbg הוא כלי דיבאגינג בעל יכולות מתקדמות המופץ חינמית על ידי מיקרוסופט. במקור, הוא יועד לעבודות דיבאג

# June 27, 2009 11:23 AM

Liran Chen's Blog כתב/ה:

WinDbg הוא כלי דיבאגינג בעל יכולות מתקדמות המופץ חינמית על ידי מיקרוסופט. במקור, הוא יועד לעבודות דיבאג

# February 24, 2010 10:11 PM

Liran Chen's Blog כתב/ה:

בדוט-נט קיים מנגנון סנכרון אובייקטים מובנה. ובין אם אנחנו מודעים לזה או לא, אנחנו משתמשים בו בכל פעם

# May 27, 2010 10:36 PM
שלח תגובה

(שדה חובה)  

(שדה חובה)  

(אופציונלי)

(שדה חובה) 

Please add 8 and 2 and type the answer here:


Enter the numbers above: