c# - একটি struct উপর "নতুন" ব্যবহার করে হিপ বা স্ট্যাক এটি বরাদ্দ?




.net memory-management (6)

যখন আপনি new অপারেটরের সাথে একটি ক্লাসের একটি উদাহরণ তৈরি করেন, মেমরিটি হ্যাপে বরাদ্দ করা হয়। যখন আপনি new অপারেটরের সাথে একটি স্ট্রাকচারের একটি উদাহরণ তৈরি করেন তখন মেমরিটি কি বরাদ্দ বা স্ট্যাকে বরাদ্দ করা হয়?


আমি সম্ভবত এখানে কিছু অনুপস্থিত কিন্তু কেন আমরা বরাদ্দ যত্ন?

মূল্যের মান মান দ্বারা পাস করা হয়;) এবং এইভাবে তারা সংজ্ঞায়িত করা হয় তার চেয়ে একটি ভিন্ন সুযোগে পরিবর্তন করা যাবে না। মানটি পরিবর্তন করতে সক্ষম হবার জন্য আপনাকে [ref] কীওয়ার্ড যুক্ত করতে হবে।

রেফারেন্স ধরন রেফারেন্স দ্বারা পাস করা হয় এবং পরিবর্তন করা যেতে পারে।

অবশ্যই অপরিবর্তনীয় রেফারেন্স ধরনের স্ট্রিং সবচেয়ে জনপ্রিয় এক হচ্ছে।

অ্যারে লেআউট / প্রাথমিককরণ: মানের ধরন -> শূন্য মেমরি [নাম, জিপ] [নাম, জিপ] উল্লেখের ধরন -> শূন্য মেমরি -> নল [রেফারেন্স] [রেফারেন্স]


একটি class বা struct declaration একটি ব্লুপ্রিন্ট যা রান সময় সময়ে ঘটনা বা বস্তু তৈরি করতে ব্যবহৃত হয়। আপনি যদি একজন class বা struct ব্যক্তিকে সংজ্ঞায়িত করেন তবে ব্যক্তিটি এই নামটির নাম। যদি আপনি একটি পরিবর্তনশীল পি ধরনের ব্যক্তি ঘোষণা এবং প্রাথমিকভাবে শুরু করেন, পিটিকে ব্যক্তি বা বস্তুর দৃষ্টান্ত বলে মনে করা হয়। একই ব্যক্তির প্রকারের একাধিক উদাহরণ তৈরি করা যেতে পারে এবং প্রতিটি ইনস্ট্যান্সের তার properties এবং fields বিভিন্ন মান থাকতে পারে।

একটি class একটি রেফারেন্স টাইপ। যখন class একটি বস্তু তৈরি করা হয়, যে পরিবর্তনশীল বস্তুটি বরাদ্দ করা হয় সেটি কেবল সেই মেমরির একটি রেফারেন্স ধারণ করে। বস্তু রেফারেন্স একটি নতুন পরিবর্তনশীল বরাদ্দ করা হয়, নতুন পরিবর্তনশীল মূল বস্তু বোঝায়। একটি পরিবর্তনশীলের মাধ্যমে করা পরিবর্তন অন্যান্য পরিবর্তনশীল প্রতিফলিত হয় কারণ তারা উভয় একই তথ্যকে উল্লেখ করে।

একটি গঠন একটি মান টাইপ। যখন একটি struct তৈরি হয়, পরিবর্তনশীল যা struct নির্ধারিত হয় struct এর প্রকৃত তথ্য ধারণ করে। যখন গঠন একটি নতুন পরিবর্তনশীল বরাদ্দ করা হয়, এটি অনুলিপি করা হয়। নতুন পরিবর্তনশীল এবং মূল পরিবর্তনশীল একই তথ্য দুটি পৃথক কপি থাকে। এক কপি করা পরিবর্তন অন্য অনুলিপি প্রভাবিত করে না।

সাধারণভাবে, classes আরো জটিল আচরণ মডেল বা class অবজেক্ট তৈরি হওয়ার পরে সংশোধন করা উদ্দেশ্যে তৈরি করা হয় এমন মডেলের জন্য ব্যবহৃত হয়। কাঠামোটি ছোট তথ্য কাঠামোর জন্য সর্বোত্তম উপযুক্ত যা প্রাথমিকভাবে ডেটা ধারণ করে যা গঠনটি তৈরি হওয়ার পরে সংশোধন করা হয় না।

আরো বেশী...


এটি কম্প্যাক্টভাবে রাখতে, নতুনটি structs জন্য একটি ভুল নামক, নতুন কলিং সহজভাবে কন্সট্রাকটর কল। স্ট্রাকচারের জন্য একমাত্র স্টোরেজ অবস্থান এটি সংজ্ঞায়িত অবস্থান।

যদি এটি একটি সদস্য ভেরিয়েবল হয় তবে এটি যে কোনও সংখ্যার মধ্যে সরাসরি সংরক্ষিত থাকে, যদি এটি একটি স্থানীয় পরিবর্তনশীল বা প্যারামিটার এটি স্ট্যাকে সংরক্ষণ করা হয়।

ক্লাসগুলিতে এইটিকে তুলনা করুন, যেখানে কোনও রেফারেন্স থাকবে যেখানে স্ট্রাকটি সম্পূর্ণভাবে সংরক্ষণ করা হয়েছে, যখন রেফারেন্স হিপের কোথাও কোথাও থাকে। (স্ট্যাকের মধ্যে স্থানীয় / পরামিতি মধ্যে সদস্য)

এটি C ++ তে একটি বিট দেখতে সাহায্য করতে পারে, যেখানে ক্লাস / struct এর মধ্যে প্রকৃত পার্থক্য নেই। (ভাষাতে একই নাম আছে, তবে তারা শুধুমাত্র জিনিসগুলির ডিফল্ট অ্যাক্সেসিবিলিটিটি বোঝায়) যখন আপনি নতুন কল করেন তখন আপনি হিপ অবস্থানের জন্য পয়েন্টার পাবেন, যদি আপনার কোন পয়েন্টার রেফারেন্স থাকে তবে এটি সরাসরি স্ট্যাকে সংরক্ষণ করা হয় বা অন্যান্য বস্তুর মধ্যে, এল # মধ্যে CLA structs।


ঠিক আছে, দেখি আমি এটাকে আরও পরিষ্কার করে তুলতে পারি কিনা।

প্রথমত, অ্যাশ সঠিক: প্রশ্ন মান ভেরিয়েবল বরাদ্দ করা হয় যেখানে হয় না । এটি একটি ভিন্ন প্রশ্ন - এবং এক যার উত্তর শুধু "স্ট্যাকের" নয়। এটির চেয়ে আরও জটিল (এবং C # 2 দ্বারা আরও জটিল)। আমার এই বিষয়ে একটি নিবন্ধ আছে এবং যদি অনুরোধ করা হয় তবে এটিকে সম্প্রসারিত করব, তবে শুধুমাত্র new অপারেটরের সাথে মোকাবিলা করি।

দ্বিতীয়ত, এই সব আপনি কি স্তরের কথা বলছেন উপর নির্ভর করে। আমি কম্পাইলার সোর্স কোডের সাথে কি করে তা দেখছি, এটি আইএল তৈরির শর্তে। জিট কম্পাইলারটি অনেক বেশি "যৌক্তিক" বরাদ্দকরণকে অপ্টিমাইজ করার শর্তে চতুর জিনিসগুলি করবে।

তৃতীয়ত, আমি জেনেরিকসকে উপেক্ষা করছি, কারণ বেশিরভাগ ক্ষেত্রেই আমি উত্তরটি আসলেই জানি না এবং আংশিকভাবে এটি জিনিসগুলিকে জটিল করে তুলবে।

অবশেষে, এই সব বর্তমান বাস্তবায়ন সঙ্গে হয়। C # spec এটির বেশিরভাগ উল্লেখ করে না - এটি কার্যকরভাবে কার্যকরকরণের বিস্তারিত। যারা পরিচালনা কোড ডেভেলপার সত্যিই যত্ন না উচিত বিশ্বাস যারা আছে। আমি নিশ্চিত যে আমি এ পর্যন্ত যেতে চাই না, তবে এটি এমন একটি কল্পনাযোগ্য মূল্য যা মূল্যবান সমস্ত স্থানীয় ভেরিয়েবলগুলি হিপে থাকে - যা এখনও স্পেকের সাথে মিলিত হবে।

মূল্যের new অপারেটরগুলির সাথে দুটি আলাদা পরিস্থিতি রয়েছে: আপনি একটি প্যারামিটারহীন কনস্ট্রাকটর (যেমন new Guid() ) বা একটি প্যারামিটারি কনস্ট্রাকটর (যেমন new Guid(someString) ) কল করতে পারেন। এই উল্লেখযোগ্যভাবে ভিন্ন আইএল উৎপন্ন। কেন বোঝার জন্য, আপনাকে C # এবং CLI স্পেসগুলি তুলনা করতে হবে: C # অনুসারে, সমস্ত মান ধরনের একটি প্যারামিটারহীন কনস্ট্রাক্টর আছে। CLI spec এর মতে, কোনও মান ধরনের প্যারামিটারহীন কনস্ট্রাক্টর নেই। (কিছু সময়ের প্রতিফলন সহ একটি মান টাইপের কন্সট্রাক্টর আনুন - আপনি একটি প্যারামিটারহীন খুঁজে পাবেন না।)

এটি C # কে কনস্ট্রাক্টর হিসাবে "জিরোসের সাথে একটি মান শুরু করুন" চিকিত্সা করে তোলে কারণ এটি ভাষা সামঞ্জস্যপূর্ণ রাখে - আপনি সর্বদা একটি কন্সট্রাকটরকে কল করার মতো new(...) ভাবতে পারেন। CLI- এর জন্য এটি ভিন্নভাবে ভাবতে পারে কারণ এটির কোনও বাস্তব কোড নেই - এবং অবশ্যই কোনও টাইপ-নির্দিষ্ট কোড নেই।

আপনি এটি শুরু করার পরে মানটির সাথে যা করতে যাচ্ছেন তাও এটি একটি পার্থক্য তৈরি করে। আইএল জন্য ব্যবহৃত

Guid localVariable = new Guid(someString);

আইএল ব্যবহৃত হয় ভিন্ন:

myInstanceOrStaticVariable = new Guid(someString);

উপরন্তু, যদি মানটি মধ্যবর্তী মান হিসাবে ব্যবহার করা হয়, উদাহরণস্বরূপ একটি পদ্ধতি কল করার জন্য একটি যুক্তি, জিনিস আবার কিছুটা ভিন্ন। এই সব পার্থক্য দেখাতে, এখানে একটি সংক্ষিপ্ত পরীক্ষা প্রোগ্রাম। এটি স্ট্যাটিক ভেরিয়েবল এবং ইনস্ট্যান্স ভেরিয়েবলগুলির মধ্যে পার্থক্য দেখায় না: IL stfld এবং stsfld মধ্যে পার্থক্য করবে, কিন্তু stsfld সব।

using System;

public class Test
{
    static Guid field;

    static void Main() {}
    static void MethodTakingGuid(Guid guid) {}


    static void ParameterisedCtorAssignToField()
    {
        field = new Guid("");
    }

    static void ParameterisedCtorAssignToLocal()
    {
        Guid local = new Guid("");
        // Force the value to be used
        local.ToString();
    }

    static void ParameterisedCtorCallMethod()
    {
        MethodTakingGuid(new Guid(""));
    }

    static void ParameterlessCtorAssignToField()
    {
        field = new Guid();
    }

    static void ParameterlessCtorAssignToLocal()
    {
        Guid local = new Guid();
        // Force the value to be used
        local.ToString();
    }

    static void ParameterlessCtorCallMethod()
    {
        MethodTakingGuid(new Guid());
    }
}

এখানে অপ্রাসঙ্গিক বিট বাদ দেওয়ার জন্য ক্লাসের জন্য IL (যেমন নপস):

.class public auto ansi beforefieldinit Test extends [mscorlib]System.Object    
{
    // Removed Test's constructor, Main, and MethodTakingGuid.

    .method private hidebysig static void ParameterisedCtorAssignToField() cil managed
    {
        .maxstack 8
        L_0001: ldstr ""
        L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string)
        L_000b: stsfld valuetype [mscorlib]System.Guid Test::field
        L_0010: ret     
    }

    .method private hidebysig static void ParameterisedCtorAssignToLocal() cil managed
    {
        .maxstack 2
        .locals init ([0] valuetype [mscorlib]System.Guid guid)    
        L_0001: ldloca.s guid    
        L_0003: ldstr ""    
        L_0008: call instance void [mscorlib]System.Guid::.ctor(string)    
        // Removed ToString() call
        L_001c: ret
    }

    .method private hidebysig static void ParameterisedCtorCallMethod() cil  managed    
    {   
        .maxstack 8
        L_0001: ldstr ""
        L_0006: newobj instance void [mscorlib]System.Guid::.ctor(string)
        L_000b: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid)
        L_0011: ret     
    }

    .method private hidebysig static void ParameterlessCtorAssignToField() cil managed
    {
        .maxstack 8
        L_0001: ldsflda valuetype [mscorlib]System.Guid Test::field
        L_0006: initobj [mscorlib]System.Guid
        L_000c: ret 
    }

    .method private hidebysig static void ParameterlessCtorAssignToLocal() cil managed
    {
        .maxstack 1
        .locals init ([0] valuetype [mscorlib]System.Guid guid)
        L_0001: ldloca.s guid
        L_0003: initobj [mscorlib]System.Guid
        // Removed ToString() call
        L_0017: ret 
    }

    .method private hidebysig static void ParameterlessCtorCallMethod() cil managed
    {
        .maxstack 1
        .locals init ([0] valuetype [mscorlib]System.Guid guid)    
        L_0001: ldloca.s guid
        L_0003: initobj [mscorlib]System.Guid
        L_0009: ldloc.0 
        L_000a: call void Test::MethodTakingGuid(valuetype [mscorlib]System.Guid)
        L_0010: ret 
    }

    .field private static valuetype [mscorlib]System.Guid field
}

আপনি দেখতে পারেন, কন্সট্রাকটরকে কল করার জন্য ব্যবহৃত বিভিন্ন নির্দেশাবলী রয়েছে:

  • newobj : স্ট্যাকের মান বরাদ্দ করে, একটি প্যারামিটারাইজড কনস্ট্রাকটর কল করে। মধ্যবর্তী মানগুলির জন্য ব্যবহৃত, যেমন একটি ক্ষেত্রের জন্য বরাদ্দকরণ বা পদ্ধতির যুক্তি হিসাবে ব্যবহার করা।
  • call instance : একটি ইতিমধ্যে বরাদ্দ স্টোরেজ অবস্থান ব্যবহার করে (স্ট্যাক বা না কিনা)। এটি একটি স্থানীয় পরিবর্তনশীল নিয়োগের জন্য উপরে কোড ব্যবহার করা হয়। কয়েকটি new কল ব্যবহার করে একই স্থানীয় ভেরিয়েবলটি বহুবার একটি মান বরাদ্দ করা হয় তবে এটি কেবল পুরানো মানের শীর্ষস্থানে ডেটা সূচনা করে - এটি প্রতিটি সময় আরো স্ট্যাক স্থান বরাদ্দ করে না
  • initobj : একটি ইতিমধ্যে বরাদ্দ স্টোরেজ অবস্থান ব্যবহার করে এবং শুধু তথ্য wipes। এটি আমাদের সমস্ত প্যারামিটারহীন কনস্ট্রাক্টর কলগুলির জন্য ব্যবহার করা হয়, যা স্থানীয় পরিবর্তনশীলকে বরাদ্দ করে। পদ্ধতি কল করার জন্য, একটি মধ্যবর্তী স্থানীয় পরিবর্তনশীল কার্যকরভাবে কার্যকর করা হয়, এবং এর মান initobj দ্বারা মুছে ফেলা হয়।

আমি আশা করি এই বিষয়টা কতটুকু জটিল বিষয় দেখাবে, যখন একই সময়ে এটির উপর হালকা আলোকপাত হবে। কিছু ধারণাগত ইন্দ্রিয়গুলিতে, new কলগুলিতে প্রতিটি কল স্ট্যাকের উপর স্থান বরাদ্দ করে - কিন্তু আমরা যেমন দেখেছি, এটি আসলেই আইএল স্তরেও ঘটে না। আমি একটি বিশেষ ক্ষেত্রে হাইলাইট করতে চাই। এই পদ্ধতি নিন:

void HowManyStackAllocations()
{
    Guid guid = new Guid();
    // [...] Use guid
    guid = new Guid(someBytes);
    // [...] Use guid
    guid = new Guid(someString);
    // [...] Use guid
}

যে "যৌক্তিকভাবে" 4 স্ট্যাক বরাদ্দ আছে - একটি পরিবর্তনের জন্য, এবং তিনটি new কলগুলির মধ্যে একটি - কিন্তু আসলে (যে নির্দিষ্ট কোডের জন্য) স্ট্যাকটি একবার বরাদ্দ করা হয় এবং তারপরে একই স্টোরেজ অবস্থান পুনঃব্যবহৃত হয়।

সম্পাদনা করুন: শুধু পরিষ্কার হতে হবে, এটি কেবল কিছু ক্ষেত্রেই সত্য ... বিশেষত, যদি Guid কন্সট্রাকটর একটি ব্যতিক্রম ছুঁড়ে ফেলে তবে Guid মান দৃশ্যমান হবে না, তাই সি # কম্পাইলার একই স্ট্যাকটি পুনঃব্যবহার করতে সক্ষম স্লট। আরও বিস্তারিত জানার জন্য এরিক লিপার্টের ব্লগ পোস্টটি মান টাইপ নির্মাণের ক্ষেত্রে এবং যেখানে এটি প্রযোজ্য নয় সেখানে দেখুন।

আমি এই উত্তরটি লেখার জন্য অনেক কিছু শিখেছি - যদি এটির কোনটি স্পষ্ট না হয় তবে স্পষ্টকরণের জন্য জিজ্ঞাসা করুন!


সমস্ত মান ধরনের সঙ্গে, তারা যেখানে ঘোষণা করা হয় structs সবসময় যান।

Structs ব্যবহার করার জন্য আরও বিস্তারিত জানার জন্য here এই প্রশ্ন দেখুন। এবং structs কিছু আরও তথ্যের জন্য here এই প্রশ্ন।

সম্পাদনা: আমি ভুলভাবে উত্তর দিয়েছি যে তারা স্ট্যাকের মধ্যে চলে যায়। এটা incorrect


স্ট্যাক স্ট্যাক বরাদ্দ পেতে। এখানে একটি সহায়ক ব্যাখ্যা:

Structs

উপরন্তু, ক্লাসগুলি যখন .NET এর মধ্যে তাত্ক্ষণিকভাবে হিপ বা নেটের সংরক্ষিত মেমরি স্পেসে মেমরি বরাদ্দ করে। স্ট্যাক উপর বরাদ্দ কারণে তাত্ক্ষণিক যখন structs আরো দক্ষতা উত্পাদন। উপরন্তু, এটা উল্লেখ করা উচিত যে structs মধ্যে পরামিতি পাশাপাশি মান দ্বারা সম্পন্ন করা হয়।





memory-management