যদিও আধুনিক মোবাইল ডিভাইসগুলোর ফিজিক্যাল র্যামের ধারণক্ষমতা ক্রমাগত বাড়ছে, উচ্চ-রেজোলিউশনের অ্যাসেট এবং জটিল রেন্ডারিং পাইপলাইনের কারণে গেম মেমোরির ব্যবহার এই বৃদ্ধির হারকে ছাড়িয়ে যাচ্ছে।
অতিরিক্ত মেমরি ব্যবহারের কারণে সৃষ্ট প্রধান সমস্যাগুলো
- লো মেমোরি কিলার (LMK) টার্মিনেশন : সিস্টেম-ব্যাপী মেমোরির ঘাটতি মেটাতে অপারেটিং সিস্টেম জোরপূর্বক ব্যাকগ্রাউন্ড বা এমনকি ফোরগ্রাউন্ড অ্যাপগুলো বন্ধ করে দেয়।
- ফ্রেম ড্রপ এবং স্টাটারিং (জ্যাঙ্ক) : ম্যানেজড হিপ-এ ঘন ঘন গার্বেজ কালেকশন (GC) স্পাইক অথবা OS-স্তরের মেমরি সোয়াপিং প্রসেসিং-এ বাধা সৃষ্টি করে।
- থার্মাল থ্রটলিং এবং ব্যাটারি ড্রেন : ক্রমাগত মেমরি অ্যালোকেশন, ডিঅ্যালোকেশন এবং মেমরি পেজ কমিটের কারণে সিপিইউ-তে উল্লেখযোগ্য ওভারহেড তৈরি হয়, যার ফলে তাপ বৃদ্ধি পায় এবং ব্যাটারি দ্রুত শেষ হয়ে যায়।
অ্যান্ড্রয়েড মেমরি ম্যানেজমেন্ট আপডেট
- অ্যান্ড্রয়েড ১৭:
MemoryLimiterপ্রবর্তন : অ্যান্ড্রয়েড ১৭-এMemoryLimiterচালু করা হয়েছে, যা ডিভাইস-নির্দিষ্ট সীমার বিপরীতে অ্যাপ্লিকেশনের মেমোরি ব্যবহার সক্রিয়ভাবে পর্যবেক্ষণ করে। যে অ্যাপগুলো তাদের মেমোরি সীমা অতিক্রম করে, সেগুলোকে সিস্টেম পর্যায়ে তাৎক্ষণিকভাবে বন্ধ করে দেওয়া হয়। যেহেতু এই ব্যবস্থাটি প্রচলিত LMK-এর চেয়ে বেশি কঠোর, তাই সর্বোচ্চ মেমোরি ব্যবহার নিয়ন্ত্রণ করা এখন আগের চেয়ে অনেক বেশি গুরুত্বপূর্ণ ।
ইউনিটিতে মেমরি ব্যবহার কমান
ইউনিটিতে, ইঞ্জিন যখন কোনো উচ্চ পিক লোড সামাল দেওয়ার জন্য তার অভ্যন্তরীণ মেমোরি পুল ( নেটিভ ব্লক অ্যালোকেটর এবং ম্যানেজড হিপ ) প্রসারিত করে, তখন এটি সেই মেমোরি পেজগুলোকে তাৎক্ষণিকভাবে অপারেটিং সিস্টেমে ফেরত না দিয়ে ধরে রাখে। ফলস্বরূপ, ভারী অ্যাসেটগুলো আনলোড করার পরেও বেসলাইন রেসিডেন্ট মেমোরি স্ফীত থেকে যায়, যা অ্যাপ্লিকেশনটিকে অ্যান্ড্রয়েড ওএস প্রসেস টার্মিনেশনের (যেমন LMK বা MemoryLimiter ) প্রতি অত্যন্ত ঝুঁকিপূর্ণ করে তোলে।
ওএস-স্তরের বাধ্যবাধকতা রোধ করতে, মেমরি অপ্টিমাইজেশনকে অবশ্যই তিনটি মূল স্তম্ভের মাধ্যমে এগিয়ে নিয়ে যেতে হবে:
- ক্যাটাগরি এ: সর্বোচ্চ মেমরি ব্যবহার হ্রাস করুন
- বিভাগ বি: অপ্রয়োজনীয় সম্পদ এবং সিস্টেমের পদচিহ্ন দূর করুন
- বিভাগ C: অপ্রয়োজনীয় GC বরাদ্দ দূর করুন
ক্যাটাগরি এ: সর্বোচ্চ মেমরি ব্যবহার হ্রাস করুন
ইউনিটির অ্যালোকেটরগুলো মুক্ত করা মেমোরি পেজগুলোকে সাথে সাথে অপারেটিং সিস্টেমে ফেরত না দিয়ে, পুনরায় ব্যবহারের জন্য ধরে রাখে। তাই বেসলাইন মেমোরি বর্তমান ব্যবহারের পরিবর্তে সর্বোচ্চ ব্যবহারের পরিমাণকেই প্রতিফলিত করে। সুতরাং, ঘটনার পরে পরিষ্কার করার উপর নির্ভর করার চেয়ে শুরুতেই হঠাৎ ব্যবহার বৃদ্ধি রোধ করা বেশি কার্যকর।
১. অতিরিক্ত বড় অ্যাসেটবান্ডেল পরিহার করুন
ইউনিটির বান্ডল লোডিং মেকানিজমের কারণে, বিশাল আকারের অ্যাসেটবান্ডলগুলো মারাত্মক মেমোরি স্ফীতি এবং OOM ক্র্যাশের কারণ হয়। কার্যকর রিসোর্স ম্যানেজমেন্ট নিশ্চিত করতে বান্ডলগুলোকে মডিউলার রাখুন।
মূল বিষয়গুলি
- উচ্চ মেমরি ওভারহেড : একটিমাত্র ক্ষুদ্র অ্যাসেটের অনুরোধ করলে ইউনিটি সম্পূর্ণ বান্ডেল ফাইলটি (হেডার, মেটাডেটা এবং স্ট্রিমিং বাফার সহ) র্যামে লোড করতে বাধ্য হয়।
- আনলোড ট্র্যাপ : যদি কোনো বান্ডেলের মধ্যে থাকা কোনো অ্যাসেট সক্রিয়ভাবে ব্যবহৃত হয়, তবে পুরো বান্ডেলটি আনলোড করা যায় না , ফলে অব্যবহৃত ডেটা র্যামে আটকে থাকে।
সর্বোত্তম অনুশীলন
- বান্ডেলগুলিকে মডিউলার রাখুন : দৃশ্য বা জীবনচক্র অনুসারে অ্যাসেটগুলিকে যৌক্তিকভাবে গোষ্ঠীভুক্ত করুন।
- ইউনিটি ৬.৬+ টিপস : অনাকাঙ্ক্ষিত ক্রস-বান্ডেল নির্ভরতা এড়াতে কন্টেন্ট ডিরেক্টরি ব্যবহার করুন।
২. ScriptableObjects -এ অ্যাসেট রেফারেন্স অপ্টিমাইজ করুন
একটি ScriptableObject এর মধ্যে সরাসরি UnityEngine.Object দ্বারা সিরিয়ালাইজড ফিল্ডগুলো সরাসরি হার্ড রেফারেন্স তৈরি করে, যার ফলে ScriptableObject লোড বা ইনস্ট্যানশিয়েট হওয়ার সাথে সাথেই রেফারেন্সকৃত সমস্ত অ্যাসেট র্যামে লোড হতে বাধ্য হয়।
// 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;
}
৩. অডিও ক্লিপ লোডের ধরণ কনফিগার করুন
সমস্ত অডিও ক্লিপ অসংকুচিত অবস্থায় সরাসরি মেমরিতে লোড করলে মেমরিতে বড় ও স্থায়ী চাপ সৃষ্টি হয়। অডিও লোড সেটিংস সেগুলোর ব্যবহারের ধরনের ওপর ভিত্তি করে কনফিগার করা উচিত:
| অডিও বিভাগ | লোড টাইপ | কারণ |
|---|---|---|
| বিজিএম (পটভূমি সঙ্গীত) | স্ট্রিমিং | মেমরি স্পাইক দূর করতে ডিস্ক থেকে ছোট ছোট বাফারে অডিও স্ট্রিম করে। |
| দীর্ঘ SFX | মেমরিতে সংকুচিত | র্যামের ব্যবহার কম রাখে এবং প্লেব্যাকের সময় তাৎক্ষণিকভাবে অডিও ডিকম্প্রেস করে। |
| সংক্ষিপ্ত ও ঘন ঘন শব্দ প্রভাব | লোডে ডিকম্প্রেস করুন | প্লেব্যাকের সময় রানটাইম সিপিইউ ওভারহেড এড়াতে লোড করার সময় অডিওকে র্যামে ডিকম্প্রেস করে। |
৪. অবজেক্ট পুলিং এবং রিলিজ কৌশল বাস্তবায়ন করুন
দৃশ্য পরিবর্তনের সময় অবজেক্ট পুলের মধ্যে থেকে যাওয়া অপ্রকাশিত ইনস্ট্যান্সগুলো অনির্দিষ্টকালের জন্য সংরক্ষিত মেমরি ধরে রাখে, যা বেসলাইন মেমরি ফুটপ্রিন্টকে অপ্রয়োজনীয়ভাবে স্ফীত করে রাখে।
- করণীয় : দৃশ্য পরিবর্তনের সময় বা কম কার্যকলাপের সময়ে পর্যায়ক্রমে অব্যবহৃত পুল করা অবজেক্টগুলো পরিষ্কার বা ছাঁটাই করুন, যাতে ইউনিটি সেই সংরক্ষিত স্থানটি অন্যান্য বরাদ্দের জন্য ফেরত দিতে বা পুনরায় ব্যবহার করতে পারে।
বিভাগ বি: অপ্রয়োজনীয় মেমরি ব্যবহার দূর করুন
অপ্রয়োজনীয় গ্রাফিক অ্যাসেট এবং রেন্ডারিং টার্গেট বাফার বাদ দিলে সরাসরি বেসলাইন মেমরি ফুটপ্রিন্ট কমে যায়।
১. রেন্ডার টেক্সচার এবং ক্যামেরা ডেপথ অপ্টিমাইজ করুন
- ডেপথ/স্টেনসিল বাফার অপসারণ করুন : যে রেন্ডার টেক্সচারগুলিতে শুধুমাত্র রঙের ডেটা প্রয়োজন, সেগুলির জন্য ডেপথ স্টেনসিল ফরম্যাটকে 'None'- এ সেট করুন।
- UI ক্যামেরা ডেপথ টেক্সচার নিষ্ক্রিয় করুন : যেসব UI ক্যামেরার জন্য ডেপথ ডেটা অপ্রয়োজনীয়, সেগুলোর ক্ষেত্রে
CopyDepthPass এবং সংশ্লিষ্ট GPU টেক্সচার মেমরি বাদ দিতে URP ক্যামেরা সেটিংসে ডেপথ টেক্সচার জেনারেশন নিষ্ক্রিয় করুন।
২. টেক্সচার এবং মেশ অপ্টিমাইজ করুন
| বিভাগ | অপ্টিমাইজেশন নির্দেশিকা |
|---|---|
| টেক্সচার সংকোচন | সর্বদা টার্গেট প্ল্যাটফর্মের কম্প্রেশন ফরম্যাট প্রয়োগ করুন (উদাহরণস্বরূপ, অ্যান্ড্রয়েডের জন্য ASTC)। |
| পঠন/লিখন সক্ষম | প্রয়োজন না হলে নিষ্ক্রিয় রাখুন। এটি সক্রিয় করলে সিপিইউ এবং জিপিইউ র্যাম জুড়ে টেক্সচার মেমরির প্রতিলিপি তৈরি হয়। |
| মিপম্যাপ | UI টেক্সচার বা একটি নির্দিষ্ট ক্যামেরা দূরত্বে স্থির থাকা অবজেক্টগুলির জন্য মিপম্যাপ নিষ্ক্রিয় করুন, যার ফলে প্রায় ৩৩% টেক্সচার মেমরি সাশ্রয় হয়। |
| মেশের জটিলতা | জিপিইউ এবং নেটিভ মেমরির ব্যবহার কমাতে অপ্রয়োজনীয় পলিগন সংখ্যা ও ভার্টেক্স স্ট্রিম হ্রাস করুন। |
৩. শেডার ভ্যারিয়েন্টগুলো বাদ দিন এবং মেমরি অপ্টিমাইজ করুন
উবার-শেডার (উদাহরণস্বরূপ, URP Lit Shader) #multi_compile এবং shader_feature কীওয়ার্ড ব্যবহার করে অসংখ্য ফিচারকে একত্রিত করে। অপটিমাইজেশন ছাড়া, কম্বিনেটোরিয়াল এক্সপ্লোশনের ফলে হাজার হাজার স্বতন্ত্র শেডার ভ্যারিয়েন্ট তৈরি হয়, যার ফলে বিল্ড সাইজ বেড়ে যায়, নেটিভ মেমোরি প্রচুর পরিমাণে ব্যবহৃত হয় এবং গেমপ্লে চলাকালীন জিপিইউ ড্রাইভার কম্পাইলেশনে সমস্যা দেখা দেয়।
ক. শেডার ভ্যারিয়েন্ট মেমরি ওভারহেডের কার্যপ্রণালী
- সংমিশ্রণগত বিস্ফোরণ : প্রতিটি নতুন কীওয়ার্ড গ্রুপ যুক্ত হওয়ার সাথে সাথে মোট সম্ভাব্য রূপভেদ সূচকীয় হারে বৃদ্ধি পায়।
- চাঙ্ক বরাদ্দকরণ স্থাপত্য : ইউনিটি কম্পাইল করা বাইনারি সংস্করণগুলোকে চাঙ্ক (ডিফল্ট: ৪ মেগাবাইট) নামক সংকুচিত মেমরি ব্লকে বিভক্ত করে।
- নেটিভ মেমরি ব্লট : যখন রানটাইম কোড কোনো চাঙ্কের মধ্যে থাকা একটিমাত্র ভ্যারিয়েন্টের জন্যও অনুরোধ করে, তখন সম্পূর্ণ ৪ মেগাবাইটের চাঙ্কটি র্যামে ডিকম্প্রেস করা হয় । অপটিমাইজ করা না হলে, ঐ চাঙ্কগুলোতে থাকা হাজার হাজার অব্যবহৃত ভ্যারিয়েন্ট স্থায়ীভাবে নেটিভ মেমরি দখল করে নেয়।
বি. ইউনিটির বিল্ট-ইন মাল্টি-স্টেজ স্ট্রিপিং পাইপলাইন : ইউনিটি টার্গেট প্ল্যাটফর্মের গ্রাফিক্স সেটিংস এবং অব্যবহৃত ইঞ্জিন ফিচারের (যেমন, ফগ, লাইটম্যাপ এবং এক্সআর সেটিংস) উপর ভিত্তি করে বিল্ড টাইমে স্বয়ংক্রিয়ভাবে অপ্রয়োজনীয় ভ্যারিয়েন্টগুলো বাদ দেয়। এছাড়াও, যদি প্রজেক্টের কোনো মেটেরিয়ালে shader_feature ভ্যারিয়েন্টগুলোর কীওয়ার্ড সক্রিয়ভাবে ব্যবহৃত না হয়, তবে সেগুলো স্বয়ংক্রিয়ভাবে ফিল্টার হয়ে যায়, অন্যদিকে #multi_compile ভ্যারিয়েন্টগুলো ব্যবহার নির্বিশেষে জোরপূর্বক অন্তর্ভুক্ত করা হয়।
সি. কাস্টম স্বয়ংক্রিয় স্ট্রিপিং আর্কিটেকচার ( IPreprocessShaders ) যেহেতু স্ট্যাটিক অ্যানালাইসিস রানটাইম C# স্ক্রিপ্ট ( Material.EnableKeyword ) ব্যবহার করে ডায়নামিকভাবে পরিবর্তিত কীওয়ার্ডগুলি সনাক্ত করতে পারে না, তাই স্ট্যান্ডার্ড স্ট্রিপিং প্রায়শই অপর্যাপ্ত হয়। শুধুমাত্র প্রকৃতপক্ষে ব্যবহৃত ভ্যারিয়েন্টগুলি অন্তর্ভুক্ত করা নিশ্চিত করতে, আপনি Player.log (এডিটর সেটিংসে Log Shader Compilation সক্রিয় করে) বা Profiler Traces ( Shader.CreateGPUProgram markers) ব্যবহার করে QA টেস্ট স্যুটের সময় ভ্যারিয়েন্টগুলি সংগ্রহ করতে পারেন। তারপর, রানটাইমে কখনও এক্সিকিউট না হওয়া ভ্যারিয়েন্টগুলিকে ফিল্টার করে বাদ দিতে এবং বিল্ডে শুধুমাত্র প্রয়োজনীয়গুলি রাখতে একটি এডিটর স্ক্রিপ্টে IPreprocessShaders.OnProcessShader প্রয়োগ করুন।
বিভাগ C: অপ্রয়োজনীয় 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 অ্যাক্সেসরের কারণে সৃষ্ট বক্সিং এবং এনুমেটর হিপ অ্যালোকেশন এড়িয়ে চলুন।
// 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;
}
৪. অতিরিক্ত জিসি বরাদ্দ প্রতিরোধ নিয়মাবলী
-
Camera.allCamerasব্যবহার করা এড়িয়ে চলুন, কারণ এটি প্রতিবার কল করার সময় হিপ-এ একটি নতুনCamera[]অ্যারে তৈরি করে। এর পরিবর্তে, একটি ক্যামেরা অ্যারে ক্যাশ করুন এবং সেটিCamera.GetAllCameras(_allCameras)-এ পাস করুন। -
IReadOnlyList<T>-এforeachব্যবহার করা থেকে বিরত থাকুন : একটি ইন্টারফেসের উপর পুনরাবৃত্তি করলে struct enumerator boxing হয়, যা GC allocation তৈরি করে। এর পরিবর্তে একটি সাধারণforloop ব্যবহার করুন। - কো-রুটিন অবজেক্ট ক্যাশ করুন : বারবার
yield return new WaitForSeconds(time);ইনস্ট্যানশিয়েট করার পরিবর্তেWaitForSecondsইনস্ট্যান্সগুলো ক্যাশ করুন। - ডিকশনারিতে কাস্টম স্ট্রাক্ট কী : ডিকশনারির কী হিসেবে কাস্টম স্ট্রাক্ট ব্যবহার করলে ডিফল্ট
Equalsকল হয়, যা অবজেক্ট বক্সিং সক্রিয় করে।IEqualityComparer<T>ইমপ্লিমেন্ট করুন এবং এটিকে ডিকশনারি কনস্ট্রাক্টরে পাস করুন।
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());
৫. জেনেরিক এবং রিফ্লেকশন ব্যবহার হ্রাস করুন
জেনেরিক মেথডগুলো কোডের চমৎকার পুনঃব্যবহার ও রক্ষণাবেক্ষণযোগ্যতা প্রদান করলেও, ইউনিটি IL2CPP (সি++ এর মধ্যবর্তী ভাষা) ব্যাকএন্ডের প্রেক্ষাপটে এগুলোর অতিরিক্ত ব্যবহার আপনার প্রোজেক্টকে নেতিবাচকভাবে প্রভাবিত করতে পারে।
- IL2CPP কোড স্ফীতি : প্রতিটি অনন্য জেনেরিক টাইপ সংমিশ্রণের জন্য, IL2CPP কোডের একটি বিশেষায়িত সংস্করণ তৈরি করে। জটিল জেনেরিকের অতিরিক্ত ব্যবহারের ফলে তৈরি হওয়া C++ কোডের একটি "কম্বিনেটোরিয়াল এক্সপ্লোশন" হতে পারে, যা অ্যাপ্লিকেশনটির বাইনারি সাইজ এবং নেটিভ মেমরি ফুটপ্রিন্ট উল্লেখযোগ্যভাবে বাড়িয়ে দেয়।
- রিফ্লেকশন ওভারহেড : রিফ্লেকশন ব্যবহারকারী মেথডগুলো, যেমন
System.ReflectionAPI, স্বভাবতই ধীরগতির এবং প্রায়শই রানটাইমে হিপ অ্যালোকেশনের কারণ হয়। - সর্বোত্তম অনুশীলন : জেনেরিকস বিচক্ষণতার সাথে ব্যবহার করুন—ব্যাপক ও নির্বিচার প্রয়োগের পরিবর্তে আর্কিটেকচারাল স্বচ্ছতার জন্য এগুলিকে অগ্রাধিকার দিন। যেখানে পারফরম্যান্স অত্যন্ত গুরুত্বপূর্ণ, সেখানে কনক্রিট টাইপ বা ইন্টারফেস-ভিত্তিক পলিমরফিজমকে প্রাধান্য দিন। রিফ্লেকশনের জন্য, আপডেট লুপে কোয়েরি করার পরিবর্তে ইনিশিয়ালাইজেশনের সময়
MethodInfoবাFieldInfoমতো ফলাফল ক্যাশ করে রাখুন।
৬. লিক হওয়া নিয়ন্ত্রিত শেল এড়িয়ে চলুন
MonoBehaviour , Texture , বা GameObject এর মতো প্রতিটি UnityEngine.Object এর একটি C# "ম্যানেজড শেল" র্যাপার থাকে, যা নেটিভ C++ ইঞ্জিনের সাথে যোগাযোগ করে।
- সমস্যাটি হলো : যদি কোনো ম্যানেজড শেল একটি স্ট্যাটিক রেফারেন্স, একটি স্থায়ী ইভেন্ট সাবস্ক্রিপশন, বা একটি অপরিষ্কার ক্লোজার দ্বারা মেমরিতে আটকে থাকে, তাহলে গার্বেজ কালেক্টর (GC) সেই মেমরি পুনরুদ্ধার করতে পারে না। এমনকি নেটিভ অবজেক্টটি ধ্বংস হয়ে গেলেও, ম্যানেজড র্যাপারটি থেকে যায়, যার ফলে "ভূতুড়ে" মেমরি লিক হয় এবং তা ম্যানেজড হিপকে স্ফীত করে তোলে।
- সমাধান : সর্বদা শক্তিশালী পরিষ্করণ পদ্ধতি প্রয়োগ করুন। অবজেক্ট ধ্বংস করার সময় বা দৃশ্য পরিবর্তন করার সময়,
-=অপারেটর ব্যবহার করে ইভেন্ট থেকে স্পষ্টভাবে আনসাবস্ক্রাইব করুন এবংUnityEngine.Objectটাইপের স্ট্যাটিক রেফারেন্সগুলো নাল করে দিন। এই পরিষ্করণ নিশ্চিত করে যে নেটিভ ইঞ্জিন তার হ্যান্ডেলটি ছেড়ে দেওয়ার পরে গারবেজ কালেক্টর (GC) সফলভাবে র্যাপারটি সংগ্রহ করতে পারে।