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