Browse by Tags
All Tags »
DEV »
.Net Internals (
RSS)
אחת הפקודות היותר שימושיות שזמינה לנו ב- SOS היא CLRStack!, שביחד עם הדגל p- מנסה להציג לנו את ערכי הפרמטרים שהועברו לכל פונקציה ב-Stack הדוט-נטי שלנו. אני אומר "מנסה" בגלל ש-SOS יציג לנו את הערכים רק אם הוא חושב שהוא הצליח למצוא את הערכים הנכונים, ואני אומר "חושב" כי במקרים מסויימים הוא פשוט הולך לטעות. במקרה שבו SOS יגיע למסקנה שהוא לא יודע מה הערך שהועבר בפרמטר, תודפס התשובה <no data>. זה קורה בגלל שלעיתים ל-SOS אין באמת דרך לדעת בוודאות מה היה ערכו של הפרמטר שהועבר לפונקציה...
בדיון שעלה לאחרונה בפורום דוט-נט בתפוז, עלתה השאלה "באיזה פרימיטיב סנכרון ה-CLR משתמש כאשר אנחנו קוראים ל-Monitor.Enter?". האם נוצר Mutex לטובת העניין? אולי Event? ואולי בכלל מדובר בפרימטיב שרץ גם ב-User Mode כגון CriticalSection? מתברר שקיימת איזשהיא אי-בהירות בנושא הזה, כך שהפוסט הזה ידגים איך ניתן לברור את התשובה לשאלה בכלים העומדים לרשותנו. לפני קרוב לשנה פרסמתי פוסט קצר שתיאר בקווים כללים את האופן בו מנגנון הנעילות של ה-CLR עובד. בקצרה, הרעיון הוא שמתוך כל בלוק Header של אובייקט דוט...
כפי שכולנו מכירים, #C דורשת שכל המשתנים הלוקאלים יאותחלו לפני השימוש בהם. עם זאת, למי שיצא להעזר ב-ildasm בשביל להציץ לתוך קוד ה-IL שהקומפיילר מייצר, בוודאי שם לב שמייד לאחר ההכרזה על שם הפונקציה, מתווספת שורה בסגנון הבא: .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 10 (0xa) .maxstack 1 .locals init ([0] int32 x) <--- localsinit flag IL_0000: ldc.i4.4 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: call void [mscorlib]System.Console::WriteLine(int32...
אחד השיעורים הראשונים שכל מפתח דוט-נט לומד לשנן, הוא ש"בכל מקרה בו רוצים לחבר הרבה מחרוזות אחת לשניה, אסור בתכלית האיסור להשתמש באופרטור +, ובמקום זה להשתמש במחלקה StringBuilder". אז אחרי הדקלום המושלם הזה, אתה שואל "אוקיי, אבל למה בעצם?", והתשובה היא בדרך כלל "בגלל שמחרוזות הן Immutable, זה אומר ששימוש באופרטור+ יביא להקצאות מיותרות של מחרוזות שיהווה כל מיני 'שלבי ביניים' עד שנגיע למחרוזת הסופית". כפי שאני רואה את זה, הדקלום הזה יותר מטעה מאשר מה שהוא תורם....
לפני לא פחות מ-5 שנים, Luca Bolognese כתב פוסט שעסק בשאלה שעלתה ב-C# User Group. השאלה היתה מה יהיה הערך של x, בסוף ביצוע קטע הקוד הבא: int x = 3; x += x++; אם אנחנו זוכרים את ההבדל בין Postfix ו-Prefix, אז לא צריכה להיות יותר מדי בעיה להבין שהתוצאה תהיה בסוף 6 מאחר ואין משמעות ל++ האחרון. כך שלמעשה, ניתן לפשט את הביטוי הזה ל: x = x + x, ועדיין נקבל את אותה ההתנהגות (שימו לב שזה המקרה ב-#C. ב-CPP למשל, אין הגדרה אמיתית לגבי מה הביטוי הזה צריך להחזיר). אם כך, זאת נקודת הפתיחה שלנו. הרשתי לעצמי לקחת...
אחד החסרונות/יתרונות של דוט-נט הוא השימוש במנגנון ה-JIT (הלא הוא ה-Just in Time Compilation). למעשה, התהליך שתפקידו להפוך את ה-CIL לשפת מכונה. אפשר להסתכל על המנגנון הזה בתור יתרון מאחר ובצורה הזאת התוכנית מקומפלת על מחשב היעד שבאמת מריץ את התוכנה שלנו. בצורה הזאת, בזמן הקומפילציה ניתן להשתמש בכל היכולות שמעבד היעד תומך בהן. כלומר, בצורה הזאת אנחנו יכולים להגיע לקוד יעיל, ומהיר יותר בהשוואה לאם היינו מקמפלים את התוכנית על מחשב נפרד, בלי לדעת על אילו מחשבים יריצו את התוכנה שלנו (מאחר והיינו צריכים...
אם יש משהו אחד בסיסי שכולם יודעים על פיתוח תוכנה, הוא שלא ניתן ליצור תלות מעגלית בין פרוייקטים (DLL'ים). אם למשל יש לנו פרוייקט A, שפונה לפרוייקט B, אז לא יהיה ניתן שאותו פרוייקט B יפנה בחזרה לפרוייקט A בתור רפרנס. אם היינו עושים דבר כזה, היתה נוצרת לנו תלות מעגלית בין שני הפרוייקטים. וזה רע, בגלל שכשהקומפיילר ירצה לקמפל את A, הוא יראה שהוא תלוי ב-B, אז הוא יגש ל-B וינסה לקמפל אותו. אבל אז הוא יגלה ש-B למעשה תלוי ב-A, ואז הוא יחזור חלילה עד אין קץ.. האמנם? אם יצא לכם לפשפש מספיק במבנה הספריות...
אולי לא הייתם מיודעים לכך, אבל בכל פעם שאתם פונים ל-DateTime.Now, אתם גורמים בעקיפין הקצאת זכרון דינאמית על ידי Boxing של Int32. הסיבה לכך טמונה עמוק בתוך המימוש של Now, שאם נסתכל מקרוב, נוכל לראות שהוא למעשה עוטף קריאה ל-UtcNow והמרתו לזמן מקומי על ידי קריאה לפונקציה ToLocalTime. עם הקריאה ל-UtcNow אין בעיה, מאחר ובסך הכל נעשית קריאה פנימית ל- GetSystemTimeAsFileTime , שמחזיר את הזמן הנוכחי בפורמט UTC. הבעיה האמיתית טמונה במימוש של ToLocalTime, או למעשה במחלקה CurrentSystemTimeZone בה היא נעזרת...
והפעם, חידה. מדובר בדוגמאת קוד קטנה שמציגה התנהגות מעט.. לא צפוייה. אפשר למצוא אותה מסתובבת באינטרנט בכל מיני וריאציות משונות, אבל בשום מקום שנתקלתי בה, לא באמת סיפקו איזשהו הסבר אמיתי להתנהגות הזאת. אז בלי יותר מדי הקדמות, מה לדעתכם יהיה הפלט עבור התוכנית הבאה? static void Main( ) { char [] a = new char [0]; string b = new string (a); string c = new string ( new char [0]); string d = new string ( new char [0]); Console .WriteLine( object .ReferenceEquals(a, b)); Console .WriteLine( object .ReferenceEquals...
WinDbg הוא כלי דיבאגינג בעל יכולות מתקדמות המופץ חינמית על ידי מיקרוסופט. במקור, הוא יועד לעבודות דיבאג של תוכניות Native, אבל, כשאנחנו מצרפים לו את חבילת ההרחבה SOS (או: Son of Strike) אנחנו מקבלים תמיכה גם כן בעבודה מול תוכניות Managed, כל שבפועל, בעזרת SOS אנחנו יכולים למעשה גם לדבג את ה-CLR בעצמו. איך שאני רואה את זה, השימוש ב-SOS מתחלק ל-2 חלקים: עבודה, ומחקר. הכוונה היא שבדרך כלל, לא נצטרך להשתמש באותן יכולות (שנדבר עליהן עוד רגע) שהוא חושף לנו. בדרך כלל המקרים בהם הוא יוכל לעזור לנו, הם בניתוח...
בדוט-נט קיים מנגנון סנכרון אובייקטים מובנה. ובין אם אנחנו מודעים לזה או לא, אנחנו משתמשים בו בכל פעם שאנחנו באים לסנכרן קטעי קוד באפליקציה שלנו. המשמעות היא שאנחנו לא צריכים לגשת לאובייקטי סנכרון מובנים של מערכת ההפעלה, בכל פעם שאנחנו רוצים לעשות lock קטן בקוד. מה שמקנה לנו יתרון בביצועים בהשוואה לשימוש באובייקטי סנכרון כגון Mutex או Semaphore (במקרה שלנו, אובייקט הקרנל היחיד בו משתמשים הוא Event). עבור כל אובייקט שקיים אצלנו באפליקציה, קיים גם SyncBlock שמשוייך אליו. אותו אובייקט SyncBlock אחראי...
אובייקטים בדוט-נט אינם בנויים בתור יחידות בודדת ושלמות שנמצאות על ה-GC Heap, שמכילות את ה-Data Member'ים שהגדרנו להן. בפועל, מעבר למידע "הנראה לעין" שאנחנו מגדירים כשאנחנו כותבים מחלקה חדשה, מתווספים גם מספר שדות "בלתי נראים" לאובייקט שלנו שנמצאים באזורים שונים בזכרון. השדה הראשון והבסיסי הוא המצביע ל-MethodTable השייך לטיפוס שלנו. אני מדלג על ההסבר על ה-MethodTable והמידע שהיא מכילה (שימו לב שלא מדובר ב-VTable עצמו, אלא בטיפוס שבין היתר מכיל מצביע אליה), ובמקום זה אתמקד בנקודה...
דבר אחד שימושי בדוט-נט הוא התמיכה הטבעית הקיימת ל-Event'ים. כדי ליצור אירוע למחלקה שלנו, כל מה שעלינו לעשות הוא להגדיר Event חדש בעזרת ה-Delegate המתאים, וזהו זה. שימו לב שאפילו לא היינו צריכים לכתוב Accessors כלשהם, מאחר וכברירת מחדל, אלא אם כן לא הגדרנו אחרת, הקומפיילר יחולל כבר Accessor'ים משלו שידאגו לנהל עבורנו את הרישומים עבור האירוע שהגדרנו. זוהי נקודה שהרבה פעמים לוקחים כמובן מאליו, ולא מפנים אליה תשומת לב מיוחדת, ולמרות זאת, מסתתרת מאחוריה משמעות קריטית כש-Multithreading נכנס לתמונה...
לפני כמה פוסטים הבנו מה המשמעות של אופרטור ההשוואה (==) כשנעשה בו שימוש בקונטקסט של Reference Types. למעשה הגענו למסקנה שבמימוש הבסיס הוא למעשה עונה לנו על השאלה "האם 2 הרפרנסים שיש לי מצביעים לאותו אובייקט?", כלומר יש לנו כאן השוואה של כתובות בזכרון. אם הן זהות, קיבלנו true; אחרת, false. אבל רק רגע, בנקודה הזאת אנחנו נזכרים לרגע בפונקציה ReferenceEquals שנמצאת תחת מחלקת הבסיס Object. כמו שמשתמע מהשם, גם התפקיד שלה הוא לענות בדיוק על אותה השאלה. אז אם 2 הדרכים הללו למעשה "עושות אותו...
שאלה: האם לתוכניות שרצות בדוט-נט יש בדיקה אוטומטית עבור Arithmetic Overflows? קחו לדוגמה את התוכנית הבאה, מה לדעתכם יהיה הפלט? או שבכלל תזרק שגיאה? class Program { static void Main( string [] args) { int foo = 1; int bar = int .MaxValue + foo; Console .WriteLine(foo); } } אם הניחוש שלכם היה 2147483648- (שווה ערך ל int.MinValue), צדקתם. זאת טעות נפוצה לחשוב שכברירת מחדל הסביבה תבדוק כל הזמן האם קיימת אי התאמה בין גודל הערך שאותו אנחנו מנסים להכניס למשתנה. רוב המפתחים מצפים שעל פעולה כמו בדוגמה למעלה...