كيف تتعامل الذاكرة المدارة .net مع أنواع القيم داخل الكائنات؟

StackOverflow https://stackoverflow.com/questions/24829

  •  09-06-2019
  •  | 
  •  

سؤال

public class MyClass
{
    public int Age;
    public int ID;
}

public void MyMethod() 
{
    MyClass m = new MyClass();
    int newID;
}

وحسب فهمي فإن ما يلي صحيح:

  1. المرجع m موجود على المكدس ويخرج عن النطاق عند خروج MyMethod().
  2. نوع القيمة newID يعيش على المكدس ويخرج عن النطاق عند خروج MyMethod().
  3. الكائن الذي تم إنشاؤه بواسطة عامل التشغيل الجديد يعيش في الكومة ويصبح قابلاً للاسترداد بواسطة GC عند خروج MyMethod()، بافتراض عدم وجود مرجع آخر للكائن.

وهنا سؤالي:

  1. هل تعيش أنواع القيم داخل الكائنات على المكدس أم الكومة؟
  2. هل تعتبر أنواع قيمة الملاكمة/إلغاء الملاكمة في كائن ما مصدر قلق؟
  3. هل هناك أي موارد مفصلة، ​​ولكن مفهومة، حول هذا الموضوع؟

منطقيًا، أعتقد أن أنواع القيم داخل الفئات ستكون في الكومة، لكنني لست متأكدًا مما إذا كان يجب وضعها في صناديق للوصول إلى هناك.

يحرر:

القراءة المقترحة لهذا الموضوع:

  1. CLR عبر C # لجيفري ريختر
  2. أساسي .NET من دون بوكس
هل كانت مفيدة؟

المحلول

قيم نوع القيمة للفئة يملك للعيش مع مثيل الكائن في الكومة المُدارة.مكدس الخيط الخاص بالأسلوب يعيش فقط لمدة الطريقة؛كيف يمكن أن تستمر القيمة إذا كانت موجودة فقط داخل تلك المكدس؟

حجم كائن الفئة في الكومة المُدارة هو مجموع حقول نوع القيمة ومؤشرات النوع المرجعي ومتغيرات CLR الإضافية مثل فهرس كتلة المزامنة.عندما يقوم أحد بتعيين قيمة لحقل نوع قيمة كائن ما، يقوم CLR بنسخ القيمة إلى المساحة المخصصة داخل الكائن لهذا الحقل المحدد.

خذ على سبيل المثال، فئة بسيطة مع حقل واحد.

public class EmbeddedValues
{
  public int NumberField;
}

ومعها فئة اختبار بسيطة.

public class EmbeddedTest
{
  public void TestEmbeddedValues()
  {
    EmbeddedValues valueContainer = new EmbeddedValues();

    valueContainer.NumberField = 20;
    int publicField = valueContainer.NumberField;
  }
}

إذا كنت تستخدم MSIL Disassembler الذي توفره .NET Framework SDK لإلقاء نظرة خاطفة على رمز IL الخاص بـ EmbeddedTest.TestEmbeddedValues()

.method public hidebysig instance void  TestEmbeddedValues() cil managed
{
  // Code size       23 (0x17)
  .maxstack  2
  .locals init ([0] class soapextensions.EmbeddedValues valueContainer,
           [1] int32 publicField)
  IL_0000:  nop
  IL_0001:  newobj     instance void soapextensions.EmbeddedValues::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.s   20
  IL_000a:  stfld      int32 soapextensions.EmbeddedValues::NumberField
  IL_000f:  ldloc.0
  IL_0010:  ldfld      int32 soapextensions.EmbeddedValues::NumberField
  IL_0015:  stloc.1
  IL_0016:  ret
} // end of method EmbeddedTest::TestEmbeddedValues

لاحظ أنه تم إخبار CLR بذلك com.stfld القيمة المحملة "20" في المكدس إلى موقع حقل NumberField الخاص بـ EmbeddValues، مباشرةً في الكومة المُدارة.وبالمثل، عند استرداد القيمة، فإنه يستخدم ldfld تعليمات لنسخ القيمة مباشرةً من موقع الكومة المُدارة إلى مكدس مؤشر الترابط.لا يحدث أي صندوق/فتح علبة مع هذه الأنواع من العمليات.

نصائح أخرى

  1. توجد أي مراجع أو أنواع قيم يمتلكها الكائن في الكومة.
  2. فقط إذا كنت تقوم بإرسال ints إلى الكائنات.

أفضل مصدر رأيته لهذا هو كتاب CLR عبر C# للكاتب جيفري ريختر.إنه أمر يستحق القراءة إذا قمت بأي تطوير لـ .NET.بناءً على هذا النص، ما أفهمه هو أن أنواع القيم الموجودة داخل نوع المرجع موجودة في الكومة المضمنة في الكائن الأصلي.الأنواع المرجعية هي دائماً على الكومة.الملاكمة والفتح غير متماثلين.يمكن أن تكون الملاكمة مصدر قلق أكبر من فتح الملاكمة.ملاكمة سوف تتطلب نسخ محتويات نوع القيمة من المكدس إلى الكومة.اعتمادًا على مدى تكرار حدوث ذلك لك، قد لا يكون هناك أي فائدة من وجود بنية بدلاً من فئة.إذا كان لديك بعض التعليمات البرمجية المهمة للأداء ولم تكن متأكدًا من حدوث عملية الملاكمة وإلغاء التغليف، فاستخدم أداة لفحص كود IL الخاص بطريقتك.سترى مربع الكلمات وقم بإلغاء تحديده في IL.أنا شخصياً سأقوم بقياس أداء الكود الخاص بي وعندها فقط أرى ما إذا كان هذا مرشحًا للقلق.في حالتك، لا أعتقد أن هذه ستكون مشكلة حرجة.لن تضطر إلى النسخ من المكدس إلى الكومة (المربع) في كل مرة تصل فيها إلى نوع القيمة هذا داخل النوع المرجعي.هذا السيناريو هو حيث تصبح الملاكمة مشكلة ذات معنى أكبر.

  • الإجابة رقم 1:كومة.إعادة صياغة دون بوكس ​​من كتابه الممتاز "Essential .Net Vol 1"

تنتج الأنواع المرجعية (RT) دائمًا المثيلات المخصصة في الكومة. في المقابل، تعتمد أنواع القيم (VT) على السياق - إذا كان المتغير المحلي هو VT، فإن CLR يخصص الذاكرة على المكدس.إذا كان أحد الحقول في فئة ما عضوًا في VT، فإن CLR يخصص الذاكرة للمثيل كجزء من تخطيط الكائن/النوع الذي تم الإعلان عن الحقل فيه.

  • الإجابة رقم 2:لا.قد تحدث الملاكمة فقط عند الوصول إلى البنية عبر مرجع الكائن/مؤشر الواجهة. obInstance.VT_typedfield لن مربع.

    تحتوي متغيرات RT على عنوان الكائن الذي تشير إليه.يمكن أن يشير 2 RT var إلى نفس الكائن. وفي المقابل، فإن متغيرات VT هي المثيلات نفسها.2 VT var لا يمكن أن يشير إلى نفس الكائن (البنية)

  • الإجابة رقم 3:Don Box's Essential .net / CLR لجيفري ريختر عبر C#.عندي نسخة من السابق...على الرغم من أن الإصدار الأحدث قد يكون أكثر تحديثًا لمراجعات .Net

هل تعيش أنواع القيم داخل الكائنات على المكدس أم الكومة؟

على الكومة.إنها جزء من تخصيص أثر الكائن، تمامًا مثل المؤشرات التي تحتوي على المراجع.

هل تعتبر أنواع قيمة الملاكمة/إلغاء الملاكمة في كائن ما مصدر قلق؟

ليس هناك الملاكمة هنا.

هل هناك أي موارد مفصلة، ​​ولكن مفهومة، حول هذا الموضوع؟

+1 صوت لكتاب ريختر.

يعد المتغير أو موقع التخزين الآخر لنوع البنية عبارة عن تجميع لحقول المثيلات العامة والخاصة لهذا النوع.منح

struct Foo {public int x,y; int z;}

إعلان Foo bar; سوف يسبب bar.x, bar.y, ، و bar.z ليتم تخزينها في أي مكان bar سوف يتم تخزينها.إضافة مثل هذا الإعلان عن bar إلى فصل دراسي، من منظور تخطيط التخزين، سيكون معادلاً لإضافة ثلاثة int مجالات.في الواقع، إذا لم يفعل أحد أي شيء مع bar إلا الوصول إلى حقولها، حقول bar سوف تتصرف بنفس الطريقة التي تتصرف بها الحقول الثلاثة bar_x, bar_y, ، و bar_cantaccessthis_z [الوصول إلى الأخير يتطلب القيام بالأشياء bar بخلاف الوصول إلى حقولها، ولكنها ستشغل مساحة سواء تم استخدامها فعليًا لأي شيء أم لا].

إن التعرف على مواقع التخزين من النوع الهيكلي باعتبارها مجموعات من الحقول هو الخطوة الأولى لفهم الهياكل.قد تبدو محاولة النظر إليها على أنها تحمل نوعًا ما من الأشياء "أبسط"، ولكنها لا تتطابق مع كيفية عملها فعليًا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top