আধুনিক মোবাইল ডিভাইসের ফিজিক্যাল RAM-এর ক্ষমতা ক্রমাগত বৃদ্ধি পেলেও, হাই-রেজোলিউশন অ্যাসেট এবং জটিল রেন্ডারিং পাইপলাইনের কারণে গেমের মেমরি খরচ এই বৃদ্ধির হারকে ছাড়িয়ে যাচ্ছে।
অতিরিক্ত মেমরি ব্যবহারের কারণে হওয়া মূল সমস্যা
- লো মেমরি কিলার (LMK) টার্মিনেশন: সিস্টেম-জুড়ে মেমরির ঘাটতি মেটাতে OS ব্যাকগ্রাউন্ড বা এমনকি ফোরগ্রাউন্ড অ্যাপও জোর করে বন্ধ করে দেয়।
- ফ্রেম ড্রপ ও স্টাটারিং (জ্যাঙ্ক): ঘনঘন গার্বেজ কালেকশন (GC) ম্যানেজড হিপ বা OS-লেভেল মেমরি সোয়াপিংয়ে স্পাইক প্রসেসিং বটলনেক তৈরি করে।
- থার্মাল থ্রটলিং ও ব্যাটারি ড্রেন: একটানা মেমরি অ্যালোকেশন, ডিঅ্যালোকেশন ও মেমরি পেজ কমিট করার ফলে সিপিইউ ওভারহেড অনেক বেশি হয়, যার ফলে ডিভাইস বেশি গরম হয়ে যায় ও ব্যাটারি দ্রুত শেষ হয়ে যায়।
Android মেমোরি ম্যানেজমেন্ট আপডেট
- Android 17:
MemoryLimiter-এর সূচনা: Android 17-এMemoryLimiter-এর সূচনা করা হয়েছে যা ডিভাইস-নির্দিষ্ট থ্রেশহোল্ডের সাপেক্ষে অ্যাপ্লিকেশন মেমরি খরচ সক্রিয়ভাবে মনিটর করে। মেমরির সীমা অতিক্রম করা অ্যাপগুলি সিস্টেম লেভেলে অবিলম্বে বন্ধ করা হয়। এই মেকানিজম প্রথাগত LMK-এর চেয়ে বেশি কঠোর হওয়ার কারণে, পিক মেমরি ব্যবহার ম্যানেজ করা এখন আগের চেয়ে আরও বেশি গুরুত্বপূর্ণ।
Unity-তে মেমরির ব্যবহার কমানো
Unity-তে, ইঞ্জিন একবার তার ইন্টার্নাল মেমরি পুল (নেটিভ ব্লক
অ্যালোকেটর এবং ম্যানেজড হিপ) বাড়িয়ে নিলে, যাতে হাই পিক লোড ম্যানেজ করা যায়, এটি
সেই মেমরি পেজগুলি রেখে দেয়, সাথে সাথে OS-এ ফেরত দেয় না।
ফলস্বরূপ, ভারী অ্যাসেট আনলোড করার পরেও, বেসলাইন রেসিডেন্ট
মেমরি স্ফীত থাকে, যার ফলে অ্যাপ্লিকেশনটি Android
OS প্রসেস বন্ধ করার (যেমন LMK বা MemoryLimiter) জন্য অত্যন্ত ঝুঁকিপূর্ণ হয়ে ওঠে।
OS-লেভেল এনফোর্সমেন্ট প্রতিরোধ করতে, মেমরি অপ্টিমাইজেশন অবশ্যই তিনটি মূল স্তম্ভের মাধ্যমে সম্পন্ন করতে হবে:
- বিভাগ 'ক': পিক মেমরি ব্যবহার কমানো
- বিভাগ খ: অপ্রয়োজনীয় অ্যাসেট ও সিস্টেম ফুটপ্রিন্ট বাদ দেওয়া
- বিভাগ গ: অপ্রয়োজনীয় GC অ্যালোকেশন বাদ দেওয়া
বিভাগ ক: পিক মেমরি ব্যবহার কমানো
Unity-র অ্যালোকেটর খালি করা মেমরি পেজগুলি OS-এ ফেরত দেওয়ার পরিবর্তে আবার ব্যবহার করার জন্য রেখে দেয়, তাই বেসলাইন মেমরি বর্তমান ব্যবহারের পরিবর্তে সর্বোচ্চ সীমা দেখায়। তাই, স্পাইক হওয়ার পরে তা পরিষ্কার করার উপর নির্ভর করার চেয়ে প্রথমেই স্পাইক হওয়া আটকানো অনেক বেশি কার্যকর।
১. অত্যধিক বড় AssetBundles এড়িয়ে চলুন
Unity-র বান্ডেল লোডিং মেকানিজমের কারণে, বিশাল AssetBundles-এর ফলে গুরুতর মেমরি ব্লোট ও OOM ক্র্যাশ হয়। ভালোভাবে রিসোর্স ম্যানেজ করার জন্য বান্ডেল মডুলার রাখুন।
গুরুত্বপূর্ণ সমস্যা
- মেমরি ওভারহেড বেশি: একটিমাত্র খুব ছোট অ্যাসেটের অনুরোধ করলে, Unity-কে পুরো বান্ডেল ফাইল (যার মধ্যে হেডার, মেটাডেটা ও স্ট্রিমিং বাফার) RAM-এ লোড করতে হয়।
- আনলোড ট্র্যাপ: কোনও বান্ডেলের মধ্যে যেকোনও অ্যাসেট অ্যাক্টিভভাবে ব্যবহার করা হলে, সম্পূর্ণ বান্ডেল আনলোড করা যায় না, এর ফলে RAM-এ অব্যবহৃত ডেটা আটকে যায়।
পেশাদার পদ্ধতি
- বান্ডেল মডিউলার রাখুন: দৃশ্য বা লাইফসাইকেল অনুযায়ী অ্যাসেটকে লজিক্যালি গ্রুপ করুন।
- Unity 6.6+ সংক্রান্ত পরামর্শ: অনিচ্ছাকৃত ক্রস-বান্ডেল ডিপেন্ডেন্সি প্রতিরোধ করতে কন্টেন্ট ডিরেক্টরি ব্যবহার করুন।
২. ScriptableObjects-এ অ্যাসেট রেফারেন্স অপ্টিমাইজ করুন
ScriptableObject-এ ডাইরেক্ট UnityEngine.Object সিরিয়ালাইজড ফিল্ড
ডাইরেক্ট হার্ড রেফারেন্স তৈরি করে, যার ফলে ScriptableObject লোড বা ইনস্ট্যান্টিয়েট হওয়ার সাথে সাথেই
সব রেফারেন্স করা অ্যাসেট RAM-এ লোড হতে বাধ্য হয়।
// BEFORE: Loading SceneRequiredAssets forces _worldAsset and _spawnSettings into RAM immediately
public class SceneRequiredAssets : ScriptableObject
{
public string sceneName;
public Object _worldAsset;
public Object _spawnSettings;
}
// AFTER: Use AssetReference to enable asynchronous, on-demand loading using Addressables
public class SceneRequiredAssets : ScriptableObject
{
public string sceneName;
public AssetReference _worldAsset;
public AssetReference _spawnSettings;
}
৩. অডিও ক্লিপ ও চ্যানেল কনফিগার করা
মেমরিতে সরাসরি আনকমপ্রেসড সব অডিও ক্লিপ লোড করলে, বড়, স্থায়ী মেমরি পিক তৈরি হয়। তাদের ব্যবহারের প্রোফাইলের উপর ভিত্তি করে অডিও লোড সেটিংস কনফিগার করা উচিত:
| অডিওর বিভাগ | লোড টাইপ | কারণ |
|---|---|---|
| BGM (ব্যাকগ্রাউন্ড মিউজিক) | স্ট্রিমিং | মেমরি স্পাইক এড়াতে ছোট ছোট বাফার ব্যবহার করে ডিস্ক থেকে অডিও স্ট্রিম করে। |
| দীর্ঘ SFX | মেমরিতে কম্প্রেস করা হয়েছে | RAM ওভারহেড কম রাখে এবং প্লেব্যাকের সময় অডিও ডিকম্প্রেস করে। |
| ছোট ও ঘন ঘন SFX | লোড করার সময় ডিকম্প্রেস করুন | প্লেব্যাকের সময় রানটাইম CPU ওভারহেড এড়াতে, লোড করার সময় RAM-এ অডিও ডিকম্প্রেস করে। |
লোড টাইপ ছাড়াও, মেমরি ওভারহেড আরও কমাতে নিম্নলিখিত অডিও কনফিগারেশন প্রয়োগ করুন:
- নন-ডিরেকশনাল অডিওর জন্য 'ফোর্স টু মোনো' বিকল্প চালু করা: UI সাউন্ড এফেক্ট, ভয়েস-ওভার এবং 3D স্পেশিয়ালাইজড সাউন্ড এফেক্টের মতো অনেক অডিও অ্যাসেটের ক্ষেত্রে স্টেরিও চ্যানেল আলাদা করার প্রয়োজন হয় না। এইসব অ্যাসেটের মেমরি ফুটপ্রিন্ট ও বাফার সাইজ অর্ধেক (৫০% কমানো) করতে
AudioClipইমপোর্ট সেটিংসে ফোর্স টু মোনো বিকল্প চালু করুন। - ডিভাইস টিয়ার অনুযায়ী অ্যাক্টিভ অডিও চ্যানেল থ্রটল করা: প্রতিটি অ্যাক্টিভ অডিও ভয়েস
নেটিভ DSP মিক্সার ও ডিকম্প্রেশন বাফার অ্যাসাইন করে। কম মেমরি থাকা ডিভাইসে,
একসাথে চালু থাকা চ্যানেলের সর্বাধিক সংখ্যা কমান—যেমন, Unity-তে
AudioSettings.Resetব্যবহার করে সর্বাধিক রিয়েল ভয়েস (যেমন, ৩২ থেকে ১৬) কমিয়ে অথবা থার্ড-পার্টি অডিও ইঞ্জিনে সফ্টওয়্যার প্লেব্যাক চ্যানেল সীমাবদ্ধ করে।
৪. অবজেক্ট পুলিং ও রিলিজ স্ট্র্যাটেজি প্রয়োগ করা
সিন ট্রানজিশন জুড়ে অবজেক্ট পুলে অবশিষ্ট থাকা আনরিলিজ করা ইনস্ট্যান্সগুলি অনির্দিষ্টকালের জন্য রিজার্ভ করা মেমরি ধরে রাখে, যার ফলে বেসলাইন মেমরি ফুটপ্রিন্ট অযথা বেড়ে যায়।
- অ্যাকশন: সিন ট্রানজিশন বা কম অ্যাক্টিভিটি চলাকালীন, মাঝে মাঝে অব্যবহৃত পুল করা অবজেক্ট মুছে দিন বা ট্রিম করুন, যাতে Unity সেই রিজার্ভ করা স্পেস অন্যান্য অ্যালোকেশনের জন্য ফেরত দিতে বা আবার ব্যবহার করতে পারে।
বিভাগ খ: অপ্রয়োজনীয় মেমরির ব্যবহার বন্ধ করা
অপ্রয়োজনীয় গ্রাফিক অ্যাসেট বাদ দেওয়া এবং সরাসরি টার্গেট বাফার রেন্ডার করা বেসলাইন মেমরি ফুটপ্রিন্ট কমায়।
১. রেন্ডার টেক্সচার ও ক্যামেরা ডেপথ অপ্টিমাইজ করুন
- ডেপথ/স্টেনসিল বাফার সরিয়ে দিন: রেন্ডার টেক্সচারের জন্য ডেপথ স্টেনসিল ফর্ম্যাট কিছুই নয় হিসেবে সেট করুন যার জন্য শুধুমাত্র রঙের ডেটা প্রয়োজন।
- UI ক্যামেরা ডেপথ টেক্সচার বন্ধ করা: UI ক্যামেরার ক্ষেত্রে যেখানে ডেপথ ডেটা
প্রয়োজন নেই, সেখানে URP ক্যামেরা সেটিংসে ডেপথ টেক্সচার জেনারেশন বন্ধ করুন যাতে
CopyDepthপাস এবং সংশ্লিষ্ট GPU টেক্সচার মেমরি বাদ দেওয়া যায়।
২. টেক্সচার ও মেশ অপ্টিমাইজ করুন
| বিভাগ | অপ্টিমাইজেশন সংক্রান্ত নির্দেশিকা |
|---|---|
| টেক্সচার কম্প্রেশন | টার্গেট প্ল্যাটফর্ম কম্প্রেশন ফর্ম্যাট (যেমন, Android-এর জন্য ASTC) সবসময় প্রয়োগ করুন। |
| পড়া/লেখার অ্যাক্সেস চালু করা আছে | প্রয়োজন না হলে বন্ধ রাখুন। এটি চালু করলে সিপিইউ ও জিপিইউ র্যাম জুড়ে টেক্সচার মেমরি ডুপ্লিকেট করে। |
| Mipmaps | UI টেক্সচার বা অবজেক্টের জন্য মিপম্যাপ বন্ধ করুন যা ক্যামেরার থেকে একটি নির্দিষ্ট দূরত্বে থাকে, এটি ~৩৩% টেক্সচার মেমরি বাঁচায়। |
| মেশ কমপ্লেক্সিটি | GPU ও নেটিভ মেমরি ফুটপ্রিন্ট কমাতে অপ্রয়োজনীয় বহুভুজ সংখ্যা ও ভার্টেক্স স্ট্রিম কমান। |
৩. স্ট্রিপ শেডার ভেরিয়েন্ট ও মেমরি অপ্টিমাইজ করা
উবার-শেডার (যেমন, URP লিট শেডার) #multi_compile এবং shader_feature কীওয়ার্ড ব্যবহার করে
অসংখ্য ফিচার এনক্যাপসুলেট করে। অপ্টিমাইজেশন ছাড়া,
কম্বিনেটোরিয়াল এক্সপ্লোশন হাজার হাজার অনন্য
শেডার ভেরিয়েন্ট তৈরি করে, যার ফলে বিল্ড সাইজ বৃদ্ধি পায়, প্রচুর পরিমাণে নেটিভ মেমরি
ব্যবহার হয় এবং গেমপ্লে চলাকালীন GPU ড্রাইভার কম্পাইলেশনে সমস্যা হয়।
উ. শেডার ভেরিয়েন্ট মেমরি ওভারহেডের মেকানিক্স
- কম্বিনেটোরিয়াল এক্সপ্লোশন: প্রতিটি যোগ করা কীওয়ার্ড গ্রুপের সাথে মোট সম্ভাব্য ভেরিয়েন্ট দ্রুত বৃদ্ধি পায়।
- চ্যাঙ্ক অ্যালোকেশন আর্কিটেকচার: Unity গ্রুপ কম্পাইল করা বাইনারি ভেরিয়েন্ট কম্প্রেস করা মেমরি ব্লকে চ্যাঙ্ক (ডিফল্ট: ৪ এমবি) বলা হয়।
- নেটিভ মেমরি ব্লোট: রানটাইম কোড যখন কোনও চঙ্কের মধ্যে একটিমাত্র ভ্যারিয়েন্টের অনুরোধ করে, তখন সম্পূর্ণ ৪ এমবি চঙ্ক RAM-এ ডিকম্প্রেস করা হয়। অপ্টিমাইজ করা না হলে, এইসব চাঙ্কে প্যাকেজ করা হাজার হাজার অব্যবহৃত ভেরিয়েন্ট নেটিভ মেমরি স্থায়ীভাবে দখল করে নেয়।
খ. Unity-র বিল্ট-ইন মাল্টি-স্টেজ স্ট্রিপিং পাইপলাইন: টার্গেট প্ল্যাটফর্ম গ্রাফিক্স সেটিংস এবং অব্যবহৃত ইঞ্জিন ফিচারের (যেমন, ফগ, লাইটম্যাপ এবং XR সেটিংস) উপর ভিত্তি করে বিল্ড টাইমে Unity অটোমেটিক অপ্রয়োজনীয় ভেরিয়েন্ট স্ট্রিপ করে। এছাড়াও, shader_featureটি ভেরিয়েন্ট অটোমেটিক ফিল্টার করে বাদ দেওয়া হয়, যদি সেগুলির কীওয়ার্ড প্রজেক্টের কোনও মেটেরিয়ালে সক্রিয়ভাবে ব্যবহার করা না হয়, যেখানে #multi_compileটি ভেরিয়েন্ট ব্যবহার করা হোক বা না হোক, জোরপূর্বক অন্তর্ভুক্ত করা হয়। তবে, রেন্ডারিং বা পোস্ট-প্রসেসিং ফিচার (যেমন
অতিরিক্ত আলো, রিয়েল-টাইম শ্যাডো বা অব্যবহৃত UberPost এফেক্ট) আপনার URP বা পাইপলাইন সেটিংসে
চালু থাকলে, Unity নির্ধারণ করতে পারে না যে আপনার
কন্টেন্ট আসলে সেগুলি ব্যবহার করে কিনা এবং সংশ্লিষ্ট সব #multi_compile
ভেরিয়েন্ট তৈরি করে কিনা। অডিট করুন এবং অব্যবহৃত পাইপলাইন সেটিংস বন্ধ করুন যাতে সেগুলি আপনার
প্রকৃত রেন্ডারিং প্রয়োজনীয়তার সাথে মেলে এবং অপ্রয়োজনীয় ভেরিয়েন্ট তৈরি করা আটকায়।
গ. কাস্টম অটোমেটেড স্ট্রিপিং আর্কিটেকচার
(IPreprocessShaders) কারণ স্ট্যাটিক অ্যানালিসিস, রানটাইম C# স্ক্রিপ্ট
(Material.EnableKeyword) ব্যবহার করে ডায়নামিক পরিবর্তন করা কীওয়ার্ড শনাক্ত করতে পারে না।
তাই, স্ট্যান্ডার্ড স্ট্রিপিং প্রায়শই অপর্যাপ্ত। শুধুমাত্র
আসলে ব্যবহৃত ভেরিয়েন্ট অন্তর্ভুক্ত করা হয়েছে কিনা তা নিশ্চিত করতে, আপনি Player.log (এডিটর সেটিংসে
লগ শেডার কম্পাইলেশন চালু করে) বা Profiler Traces
(Shader.CreateGPUProgram মার্কার) ব্যবহার করে QA টেস্ট সুইট চলাকালীন ভেরিয়েন্ট সংগ্রহ করতে পারেন। তারপর, রানটাইমের সময় কখনও রান করা হয়নি এমন ভ্যারিয়েন্ট ফিল্টার আউট করতে,
এডিটর স্ক্রিপ্টে IPreprocessShaders.OnProcessShader প্রয়োগ করুন,
বিল্ডে শুধু প্রয়োজনীয় ভ্যারিয়েন্ট রাখুন।
ঘ. অপ্রয়োজনীয় শেডার একত্রিত করা: লিট এবং সিম্পল
লিট-এর মতো উবার-শেডারে অনেক #multi_compile নির্দেশিকা থাকে এবং অসংখ্য ভেরিয়েন্ট তৈরি করার সময়
প্রচুর মেমরি খরচ করে:
- প্রোফাইল একই সাথে ব্যবহার করা: আপনার মেমরি ব্যবহার প্রোফাইল করে দেখুন যে দুটি শেডার একই সাথে উল্লেখযোগ্য মেমরি ব্যবহার করছে কিনা এবং আপনার প্রোজেক্টে সত্যিই দুটিরই প্রয়োজন আছে কিনা।
- একটি শেডার ব্যবহার করুন: যেখানে ফিজিক্যালি বেসড রেন্ডারিং (PBR) প্রয়োজন নেই, সেখানে আপনার অ্যাসেট এমনভাবে কনফিগার করুন যাতে অতিরিক্ত Lit শেডার ব্যবহার করতে না হয় এবং Simple Lit ব্যবহার করুন (যেমন, চেক করে দেখুন যে এক্সপোর্ট করা FBX মডেলে ইমপোর্ট করার সময় ভুল করে ডিফল্ট Lit মেটেরিয়াল প্রয়োগ করা হয়েছে কিনা)।
বিভাগ গ: অপ্রয়োজনীয় GC অ্যালোকেশন বাদ দেওয়া
ম্যানেজ করা হিপে গার্বেজ কালেকশন (GC) অ্যালোকেশন মেমরি ফ্র্যাগমেন্টেশন, হিপ এক্সপ্যানশন যা কখনও সঙ্কুচিত হয় না এবং GC পজের সময় গুরুতর ফ্রেম ড্রপের কারণ হয়।
১. ল্যাম্বডা ক্লোজার অ্যালোকেশন আটকান
ল্যাম্বডা এক্সপ্রেশন বাইরের লোকাল ভেরিয়েবল ক্যাপচার করলে, C# হিপে একটি
ইমপ্লিসিট ডিসপ্লে ক্লাস তৈরি করে। Update-এর মধ্যে এটি এক্সিকিউট করলে
প্রতিটি ফ্রেমে ক্লোজার ইন্সট্যান্স অ্যাসাইন করা হয়।
// Bad: Capturing local variable 'targetId' allocates a new closure object on the Heap every frame
void Update()
{
int targetId = 100;
Monster target = monsterList.Find(m => m.Id == targetId);
}
// Good 1: Replace with a standard 'for' loop (Recommended: 0 B allocation)
void Update()
{
int targetId = 100;
Monster target = null;
for (int i = 0; i < monsterList.Count; i++)
{
if (monsterList[i].Id == targetId)
{
target = monsterList[i];
break;
}
}
}
// Good 2: Use a static lambda (C# 9.0+) if no outer variables are captured
Monster target = monsterList.Find(static m => m.Id == 100);
২. stackalloc এবং Span ব্যবহার করুন
স্ট্যাক মেমরি ব্যবহার করে স্বল্পস্থায়ী অস্থায়ী অ্যারের জন্য হিপ অ্যালোকেশন এড়িয়ে চলুন।
// Before: Allocates an array on the Heap every call (GC Target)
Vector2[] pos = new Vector2[4];
// After: Utilizes Stack memory using System.Span (0 B Heap Allocation)
System.Span<Vector2> pos = stackalloc Vector2[4];
৩. সংগ্রহের আইটেরেশন অপ্টিমাইজ করা
LINQ এক্সটেনশন বা ReadOnlyCollection অ্যাক্সেসর দ্বারা সৃষ্ট বক্সিং এবং Enumerator হিপ অ্যালোকেশন এড়িয়ে চলুন।
// Before: LINQ Count() causes internal GetEnumerator() heap allocations
bool hasData = component != null && component.parameters.Count(parameter => parameter.overrideState) > 0;
// After: Replaced with indexer and direct loop iteration
bool hasData = HasDataOptimized(component);
private bool HasDataOptimized(TestComponent component)
{
if (component == null) return false;
var count = component.parameters.Count;
for (var i = 0; i < count; ++i)
{
if (component.parameters[i].overrideState)
return true;
}
return false;
}
৪. অতিরিক্ত GC অ্যালোকেশন প্রতিরোধ সংক্রান্ত নিয়ম
Camera.allCamerasব্যবহার করা এড়িয়ে চলুন কারণ এটি প্রতিটি কলে হিপে নতুনCamera[]অ্যারে তৈরি করে। পরিবর্তে, একটি ক্যামেরা অ্যারে ক্যাশে করুন এবং এটিCamera.GetAllCameras(_allCameras)-এ পাস করুন।IReadOnlyList<T>-এforeachএড়িয়ে চলুন: ইন্টারফেসের উপর ইটারেট করলে স্ট্রাক্ট এনুমারেটর বক্সিং হয়, যা GC অ্যালোকেশন তৈরি করে। এর পরিবর্তে স্ট্যান্ডার্ডforলুপ ব্যবহার করুন।- কোরুটিন অবজেক্ট ক্যাশে করা: বারবার
yield return new WaitForSeconds(time);ইনস্ট্যানশিয়েট করার পরিবর্তেWaitForSecondsইনস্ট্যান্স ক্যাশে করুন। - ডিকশনারিতে কাস্টম স্ট্রাক্ট কী: ডিকশনারি কী হিসেবে কাস্টম স্ট্রাক্ট ব্যবহার করলে ডিফল্ট
Equalsকল হয়, যা অবজেক্ট বক্সিং ট্রিগার করে। ইমপ্লিমেন্টIEqualityComparer<T>করুন এবং এটি Dictionary কনস্ট্রাক্টরে পাস করুন।
public struct TypeKey
{
public int v1;
public int v2;
public class TypeKeyComparer : IEqualityComparer<TypeKey>
{
public bool Equals(TypeKey x, TypeKey y) => x.v1 == y.v1 && x.v2 == y.v2;
public int GetHashCode(TypeKey obj) => obj.v1.GetHashCode() ^ obj.v2.GetHashCode();
}
}
// Pass custom comparer during Dictionary initialization to prevent boxing
public readonly Dictionary<TypeKey, int> _typeKeyDictionary = new(new TypeKey.TypeKeyComparer());
৫. জেনেরিক ও রিফ্লেকশনের ব্যবহার কমানো
যদিও জেনেরিক পদ্ধতিগুলি কোড আবার ব্যবহার করা এবং রক্ষণাবেক্ষণের ক্ষেত্রে খুব ভাল, তবে সেগুলি অতিরিক্ত ব্যবহার করলে Unity IL2CPP (ইন্টারমিডিয়েট ল্যাঙ্গুয়েজ থেকে C++) ব্যাকএন্ডের ক্ষেত্রে আপনার প্রোজেক্টের উপর নেতিবাচক প্রভাব পড়তে পারে।
- IL2CPP কোড ব্লোট: প্রতিটি অনন্য জেনেরিক টাইপ কম্বিনেশনের জন্য, IL2CPP কোডের একটি বিশেষ ভার্সন তৈরি করে। জটিল জেনেরিক অতিরিক্ত ব্যবহার করলে, জেনারেট করা C++ কোডের "কম্বিনেটরিয়াল এক্সপ্লোশন" হতে পারে, যার ফলে অ্যাপ্লিকেশনটির বাইনারি সাইজ এবং নেটিভ মেমরি ফুডপ্রিন্ট উল্লেখযোগ্যভাবে বেড়ে যায়।
- রিফ্লেকশন ওভারহেড: রিফ্লেকশন ব্যবহার করে এমন পদ্ধতি, যেমন
System.ReflectionAPI, সহজাতভাবে ধীরগতির এবং প্রায়শই রানটাইমের সময় হিপ অ্যালোকেশন ঘটায়। - পেশাদার পদ্ধতি: জেনেরিককে বিচক্ষণতার সাথে ব্যবহার করুন—
ব্যাপক, নির্বিচারে প্রয়োগের পরিবর্তে স্থাপত্যগত স্পষ্টতার জন্য সেগুলিকে অগ্রাধিকার দিন। যেখানে
পারফর্ম্যান্স অত্যন্ত গুরুত্বপূর্ণ, সেখানে কংক্রিট টাইপ বা ইন্টারফেস-ভিত্তিক
পলিমরফিজম ব্যবহার করুন। রিফ্লেকশনের জন্য, আপডেট
লুপে কোয়েরি করার পরিবর্তে ইনিশিয়ালাইজেশনের সময়
MethodInfoবাFieldInfo-এর মতো ক্যাশে ফলাফল।
৬. লিক হওয়া ম্যানেজ করা শেল এড়িয়ে চলুন
MonoBehaviour, Texture বা GameObject-এর মতো প্রতিটি UnityEngine.Object-এ
একটি C# "ম্যানেজড শেল" র্যাপার থাকে যা নেটিভ C++ ইঞ্জিনের সাথে যোগাযোগ করে।
- সমস্যা: স্ট্যাটিক রেফারেন্স, স্থায়ী ইভেন্ট সাবস্ক্রিপশন বা অপরিষ্কার ক্লোজারের মাধ্যমে মেমরিতে Managed Shell থাকলে, GC মেমরি রিক্লেম করতে পারে না। এমনকি নেটিভ অবজেক্ট ধ্বংস হয়ে গেলেও, ম্যানেজড র্যাপার থেকে যায়, যার ফলে "ভূত" মেমরি লিক হয় যা ম্যানেজড হিপকে ফুলিয়ে ফাঁপিয়ে তোলে।
- সমাধান: সবসময় শক্তিশালী ক্লিনআপ প্যাটার্ন প্রয়োগ করুন। অবজেক্ট ধ্বংস করার সময়
বা দৃশ্য পরিবর্তন করার সময়,
-=অপারেটর ব্যবহার করে ইভেন্ট থেকে স্পষ্টভাবে আনসাবস্ক্রাইব করুন এবংUnityEngine.Objectধরনের স্ট্যাটিক রেফারেন্স বাতিল করুন। এই ক্লিন-আপ নিশ্চিত করে যে নেটিভ ইঞ্জিন তার হ্যান্ডেল রিলিজ করার পরে GC র্যাপারটি সফলভাবে সংগ্রহ করতে পারবে।
৭. ঠিকানাযোগ্য আইটেমের জন্য বাইনারি ক্যাটালগ ব্যবহার করা
Unity 2022 ও এর আগের ভার্সনে, Addressables ডিফল্ট হিসেবে বাইনারি ক্যাটালগের পরিবর্তে
JSON ক্যাটালগ ব্যবহার করে (Unity 2023 ও এর পরের ভার্সনে বাইনারি ক্যাটালগ ডিফল্ট হিসেবে ব্যবহার করা হয়)। আপনার প্রোজেক্টে JSON ক্যাটালগ ব্যবহার করা হলে, স্টার্টআপে একটি বড় ক্যাটালগ লোড করার সময় সম্পূর্ণ ফাইলটি একটি বিশাল C# স্ট্রিংয়ে পড়ে এবং JSON পার্সিং ও Base64 ডিকোডিংয়ের সময় হাজার হাজার ম্যানেজ করা স্ট্রিং এবং অস্থায়ী byte[] অ্যারে বরাদ্দ করে। এর ফলে
GC অ্যালোকেশন স্পাইক অনেক বেড়ে যায় যা ম্যানেজ করা হিপকে স্থায়ীভাবে বাড়িয়ে দেয়,
তাই সরাসরি কম্প্যাক্ট ডেটা স্ট্রিম করতে বাইনারি ক্যাটালগে পাল্টে নিন।