জাভাতে "ফু f = নতুন ফু ()" তে অবজেক্টের সূচনা কি মূলত সি এর পয়েন্টারের জন্য মলোক ব্যবহারের মতো?


9

আমি জাভাতে অবজেক্ট তৈরির পিছনে আসল প্রক্রিয়াটি বোঝার চেষ্টা করছি - এবং আমি মনে করি যে অন্যান্য প্রোগ্রামিং ভাষা রয়েছে।

আপনি যখন সি এর কাঠামোর জন্য মলোক ব্যবহার করেন তখন জাভাতে অবজেক্টের সূচনাটি একই বলে ধরে নেওয়া কি ভুল হবে?

উদাহরণ:

Foo f = new Foo(10);
typedef struct foo Foo;
Foo *f = malloc(sizeof(Foo));

এই কারণেই বস্তুগুলি স্ট্যাকের পরিবর্তে স্তূপে রয়েছে? কারণ এগুলি মূলত কেবলমাত্র ডেটাগুলিতে পয়েন্টার?


সি # / জাভা এর মতো পরিচালিত ভাষার জন্য গাদাতে অবজেক্ট তৈরি করা হয়। সিপিসিতে আপনি স্ট্যাকের উপর ঠিক একইভাবে অবজেক্ট তৈরি করতে পারেন
बेस

জাভা / সি # এর নির্মাতারা কেন একচেটিয়াভাবে গাদাতে অবজেক্টগুলি সঞ্চয় করার সিদ্ধান্ত নিয়েছিলেন?
জুলে

আমি সরলতার জন্য মনে করি । স্ট্যাকের উপর অবজেক্টগুলি সংরক্ষণ করা এবং তাদের গভীরতর স্তরের পাশ কাটাতে স্ট্যাকের উপর অবজেক্টটি অনুলিপি করা জড়িত, যার মধ্যে অনুলিপি-নির্মাতারা জড়িত। আমি একটি সঠিক উত্তরের জন্য গুগল করিনি, তবে আমি নিশ্চিত যে আপনি নিজেই আরও সন্তোষজনক উত্তর খুঁজে পেতে পারেন (বা অন্য কেউ এই পার্শ্ব প্রশ্নটি বিস্তারিতভাবে বর্ণনা করবেন)
बेस

@ জাভাতে জুলসের বস্তুগুলি রান-টাইমে এখনও "ক্ষয়" হতে পারে (ডাকা হয় scalar-replacement) কেবল স্ট্যাকের উপরে বাস করে এমন সরল ক্ষেত্রগুলিতে; কিন্তু এটি এমন কিছু যা JITকরে, না javac
ইউজিন

বরাদ্দ বস্তু / মেমরির সাথে যুক্ত সম্পত্তিগুলির সেটগুলির জন্য কেবল "হিপ" একটি নাম। সি / সি ++ এ আপনি দুটি পৃথক বৈশিষ্ট্য থেকে "স্ট্যাক" এবং "হিপ" নামে বেছে নিতে পারেন, সি # এবং জাভাতে, সমস্ত অবজেক্টের বরাদ্দের একই নির্দিষ্ট আচরণ রয়েছে, যা "হিপ" নামে যায় যা না বোঝান যে এই বৈশিষ্ট্যগুলি সি / সি ++ "হিপ" এর মতো, বাস্তবে, তারা তা নয়। এর অর্থ এই নয় যে বাস্তবায়নের ক্ষেত্রে অবজেক্টগুলি পরিচালনার জন্য বিভিন্ন কৌশল থাকতে পারে না, এটি বোঝায় যে সেই কৌশলগুলি প্রয়োগ যুক্তির সাথে অপ্রাসঙ্গিক।
হলগার

উত্তর:


5

সি তে, malloc()গাদাতে মেমরির একটি অঞ্চল বরাদ্দ করে এবং এটিতে একটি পয়েন্টার দেয়। এটাই তুমি পেয়েছ। মেমোরিটি অনির্বাচিত এবং আপনার কোনও গ্যারান্টি নেই যে এটি সমস্ত শূন্য বা অন্য কিছু।

জাভাতে, কলিংয়ের newমতো হিপ-ভিত্তিক বরাদ্দও হয় malloc()তবে আপনি আরও এক টন অতিরিক্ত সুবিধা (বা ওভারহেড, যদি আপনি চান তবে) পান। উদাহরণস্বরূপ, আপনাকে কীভাবে বরাদ্দ করতে হবে তা স্পষ্টভাবে নির্দিষ্ট করতে হবে না। সংকলক আপনার বরাদ্দ দেওয়ার চেষ্টা করছেন এমন ধরণের অবজেক্টের ভিত্তিতে এটি আপনার জন্য নির্ধারণ করে। অতিরিক্তভাবে, অবজেক্ট কনস্ট্রাক্টরদের ডাকা হয় (যা আপনি কীভাবে আরম্ভকরণটি নিয়ন্ত্রণ করতে চান তা যুক্তি দিয়ে দিতে পারেন)। newপ্রত্যাবর্তন হলে , আপনাকে এমন কোনও বস্তুর নিশ্চয়তা দেওয়া হবে যা শুরু করা হয়েছে।

তবে হ্যাঁ, কলটির শেষে উভয়ই ফলাফলের ফলাফল malloc()এবং newকেবল হিপ-ভিত্তিক ডেটার কিছু অংশের দিকে নির্দেশ করে।

আপনার প্রশ্নের দ্বিতীয় অংশটি একটি স্ট্যাক এবং একটি স্তূপের মধ্যে পার্থক্য সম্পর্কে জিজ্ঞাসা করে। সংকলক ডিজাইনে (বা কোনও বই পড়ার) উপর কোর্স করে আরও বিস্তৃত উত্তর পাওয়া যাবে। অপারেটিং সিস্টেমের উপর একটি কোর্সও সহায়ক হবে। স্ট্যাক এবং গাদা সম্পর্কে এসও তেও রয়েছে অসংখ্য প্রশ্নোত্তর।

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

মৌলিকভাবে, দুটি মেমরি ম্যানেজমেন্ট সিস্টেম থাকার প্রধান কারণ, যেমন একটি গাদা এবং স্ট্যাক, দক্ষতার জন্য । একটি গৌণ কারণ হ'ল প্রতিটি একে অপরের তুলনায় নির্দিষ্ট ধরণের সমস্যায় ভাল।

ধারণা হিসাবে বুঝতে আমার জন্য স্ট্যাকগুলি কিছুটা সহজ, তাই আমি স্ট্যাকগুলি দিয়ে শুরু করি। আসুন সি এই ফাংশন বিবেচনা ...

int add(int lhs, int rhs) {
    int result = lhs + rhs;
    return result;
}

উপরেরটি মোটামুটি সোজা মনে হয়। আমরা নামের একটি ফাংশন সংজ্ঞায়িত করি add()এবং বাম এবং ডান সংযোজনগুলিতে পাস করি। ফাংশন তাদের যুক্ত করে এবং ফলাফল দেয়। দয়া করে সমস্ত প্রান্ত-কেস স্টাফ যেমন উপচে পড়া ওভারফ্লোগুলি উপেক্ষা করুন, এই মুহুর্তে এটি আলোচনার পক্ষে জার্মানি নয়।

add()ফাংশনের উদ্দেশ্য বেশ সহজবোধ্য মনে হলেও আমরা তার জীবনচক্র সম্পর্কে কি বলতে পারেন? বিশেষত এর স্মৃতি ব্যবহারের প্রয়োজন?

সবচেয়ে গুরুত্বপূর্ণ, সংকলক একটি প্রাইমারী (যেমন সংকলন সময়ে) জানে যে ডেটা প্রকারগুলি কত বড় এবং কতগুলি ব্যবহৃত হবে। lhsএবং rhsআর্গুমেন্ট sizeof(int), 4 প্রতিটি বাইট। চলকটিও resultহয় sizeof(int)। সংকলক বলতে পারে যে add()ফাংশনটি ব্যবহার করে 4 bytes * 3 intsবা মোট 12 বাইট মেমরি।

যখন add()ফাংশনটি বলা হয়, তখন স্ট্যাক পয়েন্টার নামে পরিচিত একটি হার্ডওয়্যার রেজিস্টারে একটি ঠিকানা থাকবে যা স্ট্যাকের শীর্ষে নির্দেশ করে। মেমরিটি বরাদ্দ করার জন্য add()ফাংশনটি চালানো দরকার, সমস্ত ফাংশন-এন্ট্রি কোড করতে হবে একটি স্ট্যাক পয়েন্টার রেজিস্টরের মান 12 দ্বারা হ্রাস করার জন্য একটি একক সমাবেশের ভাষা নির্দেশ জারি করা। এটি করার ফলে এটি স্ট্যাকের জন্য তিনটির জন্য স্টোরেজ তৈরি করে intsএক প্রত্যেকের জন্য lhs, rhsএবং result। একক নির্দেশনা সম্পাদন করে আপনার প্রয়োজনীয় মেমরির স্থান অর্জন করা গতির দিক থেকে একটি বিশাল জয় কারণ একক নির্দেশাবলী এক ঘড়ির টিকের (এক সেকেন্ডের 1 বিলিয়ন তৃতীয়াংশ 1 গিগাহার্জ সিপিইউ) প্রবাহিত করে।

এছাড়াও, সংকলকের দর্শন থেকে, এটি ভেরিয়েবলগুলিতে মানচিত্র তৈরি করতে পারে যা অ্যারের সূচিকরণের মতো ভয়াবহ দেখায়:

lhs:     ((int *)stack_pointer_register)[0]
rhs:     ((int *)stack_pointer_register)[1]
result:  ((int *)stack_pointer_register)[2]

আবার, এই সব খুব দ্রুত।

add()ফাংশনটি যখন প্রস্থান করে তখন এটি পরিষ্কার করতে হয়। এটি স্ট্যাক পয়েন্টার রেজিস্টার থেকে 12 বাইট বিয়োগ করে এটি করে। এটি একটি কলের অনুরূপ free()তবে এটি কেবলমাত্র একটি সিপিইউ নির্দেশিকা ব্যবহার করে এবং এটিতে কেবল একটি টিক লাগে। এটা খুব, খুব দ্রুত।


এখন একটি গাদা-ভিত্তিক বরাদ্দ বিবেচনা করুন। এটি যখন খেলতে আসে তখন আমরা যখন কোনও প্রাইমারী জানি না যে আমাদের কতটা মেমরির প্রয়োজন হবে (যেমন আমরা কেবল রানটাইম এ এটি শিখব)।

এই ফাংশনটি বিবেচনা করুন:

int addRandom(int count) {
    int numberOfBytesToAllocate = sizeof(int) * count;
    int *array = malloc(numberOfBytesToAllocate);
    int result = 0;

    if array != NULL {
        for (i = 0; i < count; ++i) {
            array[i] = (int) random();
            result += array[i];
        }

        free(array);
    }

    return result;
}

লক্ষ্য করুন যে addRandom()ফাংশনটি সংকলনের সময় জানে না যে countযুক্তির মান কী হবে be এ কারণে, arrayআমরা যদি এটি স্ট্যাকের উপর রাখি, তবে আমরা এটির মতো সংজ্ঞা দেওয়ার চেষ্টা করার কোনও অর্থ হয় না :

int array[count];

যদি countবিশাল হয় তবে এটি আমাদের স্ট্যাকটি খুব বড় আকার ধারণ করতে এবং অন্যান্য প্রোগ্রাম বিভাগগুলিকে ওভাররাইট করতে পারে। যখন এই স্ট্যাকের ওভারফ্লো হয় তখন আপনার প্রোগ্রামটি ক্রাশ হয় (বা আরও খারাপ)।

সুতরাং, রানটাইম হওয়া পর্যন্ত আমাদের কতটা মেমরির প্রয়োজন তা আমরা জানি না, আমরা ব্যবহার করি malloc()। তারপরে আমরা যখন আমাদের প্রয়োজন তখন আমাদের malloc()কতগুলি বাইট দরকার তা জিজ্ঞাসা করতে পারি এবং এটি যে বহু বাইট বিক্রয় করতে পারে তা পরীক্ষা করতে যাব। যদি এটি পারে তবে দুর্দান্ত, আমরা এটি ফিরিয়ে আনব, যদি তা না হয় তবে আমরা একটি NUL পয়েন্টার পাই যা আমাদের কলটি malloc()ব্যর্থ হতে বলে । উল্লেখযোগ্যভাবে যদিও, প্রোগ্রামটি ক্রাশ হয় না! অবশ্যই প্রোগ্রামার হিসাবে আপনি সিদ্ধান্ত নিতে পারেন যে যদি সংস্থান বরাদ্দ ব্যর্থ হয় তবে আপনার প্রোগ্রামটি চালানোর অনুমতি দেওয়া হয়নি, তবে প্রোগ্রামার-সূচিত সমাপ্তি একটি উদ্দীপক ক্র্যাশের চেয়ে আলাদা।

তাই এখন আমাদের দক্ষতার দিকে ফিরে আসতে হবে। স্ট্যাক বরাদ্দকারী অত্যন্ত দ্রুত - একটি নির্দেশ বরাদ্দ করার জন্য, একটি নির্দেশকে বিলোপ করার জন্য এবং এটি সংকলক দ্বারা সম্পন্ন করা হয় তবে মনে রাখবেন যে স্ট্যাকটি কোনও পরিচিত আকারের স্থানীয় ভেরিয়েবলের মতো জিনিসগুলির জন্য তাই এটি মোটামুটি ছোট হতে থাকে।

অন্যদিকে হিপ বরাদ্দকারী ধীরে ধীরে ধীরে ধীরে বিভিন্ন অর্ডার। এটির জন্য টেবিলে একটি নজরদারি করতে হবে এটি ব্যবহারকারীর যে পরিমাণ স্মৃতি মেমরি বিক্রি করতে পারে তার যথেষ্ট ফ্রি মেমরি রয়েছে কিনা তা দেখার জন্য। সেই টেবিল আপডেট করার জন্য পরে এটি মেমরি করতে vends হয়েছে নিশ্চিত কেউ অন্য যে ব্লক (এই হিসাবরক্ষণ রিজার্ভ মেমরি বরাদ্দকরণ প্রয়োজন হতে পারে ব্যবহার করতে পারেন নিজেই জন্য কি এটা বিক্রি করার পরিকল্পনা ছাড়াও)। এটি একটি থ্রেড-নিরাপদ উপায়ে মেমরিটি বিক্রে করে তা নিশ্চিত করার জন্য বরাদ্দকারীকে লক করার কৌশল ব্যবহার করতে হবে। এবং স্মৃতি অবশেষে যখনfree()ডি, যা বিভিন্ন সময়ে ঘটে এবং সাধারণত কোন পূর্বাভাসযোগ্য আদেশে না ঘটে, বরাদ্দকারীকে জঞ্জাল ব্লকগুলি সন্ধান করতে হয় এবং সেগুলির পিছনে একসাথে স্টিচ করে হিপ বিভাজনগুলি মেরামত করতে হয়। এটির মতো যদি মনে হয় যে সেগুলি সম্পূর্ণ করার জন্য এটি একটি সিপিইউ নির্দেশের চেয়ে বেশি গ্রহণ করবে, আপনি ঠিক বলেছেন! এটি খুব জটিল এবং এটি কিছুটা সময় নেয়।

তবে গাদা বড়। স্ট্যাকের চেয়ে অনেক বড়। আমরা তাদের কাছ থেকে প্রচুর স্মৃতি পেতে পারি এবং সেগুলি দুর্দান্ত হয় যখন আমরা সংকলনের সময় জানি না যে আমাদের কত স্মৃতি দরকার। সুতরাং আমরা একটি পরিচালিত মেমরি সিস্টেমের জন্য গতি বন্ধ করি যা যখন আমরা খুব বড় কিছু বরাদ্দ করার চেষ্টা করি তখন ক্র্যাশ হওয়ার পরিবর্তে নম্রভাবে আমাদের অস্বীকার করে।

আমি আশা করি এটি আপনার কয়েকটি প্রশ্নের উত্তর দিতে সহায়তা করে। আপনি উপরের যে কোনওটির বিষয়ে স্পষ্টতা চান কিনা দয়া করে আমাকে জানান।


int64-বিট প্ল্যাটফর্মে 8 বাইট নয় is এটি এখনও ৪. এর সাথে, সংকলকটি intস্ট্যাকের বাইরে থাকা তৃতীয়টি রিটার্ন রেজিস্টারে অপ্টিমাইজ করতে পারে । আসলে, দুটি যুক্তি যে কোনও 64-বিট প্ল্যাটফর্মে রেজিস্টারে থাকতে পারে।
এসএস অ্যান

আমি আমার উত্তরটি সম্পাদনা করেছি 64৪ int-বিট প্ল্যাটফর্মের 8-বাইট সম্পর্কে বিবৃতি সরাতে । আপনি সঠিক যে intজাভাতে 4-বাইট রয়ে গেছে। আমি আমার উত্তরটির বাকী অংশটি ছেড়ে দিয়েছি কারণ আমি বিশ্বাস করি যে সংকলক অপ্টিমাইজেশনে প্রবেশ করানোটি ঘোড়ার সামনে রাখে। হ্যাঁ আপনি এই বিষয়গুলিতেও সঠিক, তবে প্রশ্নটি স্ট্যাক্স বনাম হিপগুলিতে স্পষ্টতা চেয়েছে। আরভিও, রেজিস্টারগুলির মাধ্যমে পাস করার যুক্তি, কোড এলিজেন ইত্যাদি মৌলিক ধারণাগুলি বাড়িয়ে দেয় এবং মৌলিক বিষয়গুলি বোঝার পথে চলে।
পার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.