আপনার ইউনিটি গেমে LMK সমাধান করা একটি পদ্ধতিগত প্রক্রিয়া:

স্মৃতির একটি স্ন্যাপশট নিন।
ইউনিটি প্রোফাইলার ব্যবহার করে ইউনিটি-পরিচালিত মেমরির একটি স্ন্যাপশট নিন। চিত্র ২-এ আপনার গেমে মেমরি পরিচালনার জন্য ইউনিটি যে মেমরি ম্যানেজমেন্ট লেয়ারগুলো ব্যবহার করে তা দেখানো হয়েছে।

পরিচালিত স্মৃতি
ইউনিটির মেমোরি ম্যানেজমেন্ট একটি নিয়ন্ত্রিত মেমোরি লেয়ার প্রয়োগ করে, যা স্বয়ংক্রিয়ভাবে মেমোরি বরাদ্দ ও বণ্টন করার জন্য একটি ম্যানেজড হিপ এবং একটি গার্বেজ কালেক্টর ব্যবহার করে। এই ম্যানেজড মেমোরি সিস্টেমটি Mono বা IL2CPP-এর উপর ভিত্তি করে তৈরি একটি C# স্ক্রিপ্টিং পরিবেশ। ম্যানেজড মেমোরি সিস্টেমের সুবিধা হলো, এটি একটি গার্বেজ কালেক্টর ব্যবহার করে স্বয়ংক্রিয়ভাবে মেমোরি বরাদ্দ মুক্ত করে।
C# অপরিবর্তিত মেমরি
আনম্যানেজড C# মেমরি লেয়ার নেটিভ মেমরি লেয়ারে অ্যাক্সেস প্রদান করে, যা C# কোড ব্যবহার করার সময় মেমরি অ্যালোকেশনের উপর সুনির্দিষ্ট নিয়ন্ত্রণ সক্ষম করে। এই মেমরি ম্যানেজমেন্ট লেয়ারটি Unity.Collections নেমস্পেস এবং UnsafeUtility.Malloc ও UnsafeUtility.Free- এর মতো ফাংশনগুলির মাধ্যমে অ্যাক্সেস করা যায়।
স্থানীয় স্মৃতি
ইউনিটির অভ্যন্তরীণ C/C++ কোর সিন, অ্যাসেট, গ্রাফিক্স এপিআই, ড্রাইভার, সাবসিস্টেম এবং প্লাগ-ইন বাফার পরিচালনা করার জন্য একটি নেটিভ মেমরি সিস্টেম ব্যবহার করে। যদিও সরাসরি অ্যাক্সেস সীমাবদ্ধ, আপনি ইউনিটির C# এপিআই ব্যবহার করে নিরাপদে ডেটা ম্যানিপুলেট করতে পারেন এবং দক্ষ নেটিভ কোডের সুবিধা নিতে পারেন। নেটিভ মেমরির সাথে সরাসরি ইন্টারঅ্যাকশনের খুব কমই প্রয়োজন হয়, তবে আপনি প্রোফাইলার ব্যবহার করে পারফরম্যান্সের উপর নেটিভ মেমরির প্রভাব পর্যবেক্ষণ করতে পারেন এবং পারফরম্যান্স অপ্টিমাইজ করার জন্য সেটিংস সামঞ্জস্য করতে পারেন।
চিত্র ৩-এ যেমন দেখানো হয়েছে, C# এবং নেটিভ কোডের মধ্যে মেমরি শেয়ার করা হয় না। C#-এর জন্য প্রয়োজনীয় ডেটা প্রতিবার প্রয়োজনের সময় ম্যানেজড মেমরি স্পেসে বরাদ্দ করা হয়।
উদাহরণস্বরূপ, ম্যানেজড গেমের কোড (C#) যখন ইঞ্জিনের নেটিভ মেমোরি ডেটা অ্যাক্সেস করে, তখন GameObject.transform- কে কল করলে তা নেটিভ এরিয়ার মেমোরি ডেটা অ্যাক্সেস করার জন্য একটি নেটিভ কল করে এবং তারপর বাইন্ডিং ব্যবহার করে C#-এ ভ্যালু রিটার্ন করে। বাইন্ডিং প্রতিটি প্ল্যাটফর্মের জন্য সঠিক কলিং কনভেনশন নিশ্চিত করে এবং ম্যানেজড টাইপগুলোকে স্বয়ংক্রিয়ভাবে তাদের নেটিভ সমতুল্যে মার্শালিং করার কাজটি পরিচালনা করে।
এটি শুধুমাত্র প্রথমবারই ঘটে, কারণ transform প্রপার্টি অ্যাক্সেস করার জন্য ব্যবহৃত ম্যানেজড শেলটি নেটিভ কোডে সংরক্ষিত থাকে। transform প্রপার্টি ক্যাশ করলে ম্যানেজড এবং নেটিভ কোডের মধ্যে বারবার কল করার সংখ্যা কমানো যায়, কিন্তু ক্যাশিংয়ের উপযোগিতা নির্ভর করে প্রপার্টিটি কত ঘন ঘন ব্যবহৃত হয় তার উপর। এছাড়াও, মনে রাখবেন যে আপনি যখন এই API-গুলো অ্যাক্সেস করেন, তখন ইউনিটি নেটিভ মেমরির কোনো অংশ ম্যানেজড মেমরিতে কপি করে না।

আরও জানতে, ইউনিটিতে মেমরি পরিচিতি দেখুন।
এছাড়াও, আপনার গেমটি মসৃণভাবে চালানোর জন্য একটি মেমরি বাজেট নির্ধারণ করা অত্যন্ত গুরুত্বপূর্ণ, এবং একটি মেমরি ব্যবহার বিশ্লেষণ বা রিপোর্টিং সিস্টেম প্রয়োগ করা নিশ্চিত করে যে প্রতিটি নতুন রিলিজ মেমরি বাজেট অতিক্রম না করে। আরও ভালো ধারণা পাওয়ার জন্য, গেমের নির্দিষ্ট কিছু অংশে মেমরি ব্যবহার যাচাই করতে আপনার কন্টিনিউয়াস ইন্টিগ্রেশন (CI)-এর সাথে প্লে মোড টেস্টগুলিকে একীভূত করা আরেকটি কৌশল।
সম্পদ পরিচালনা করুন
মেমোরি ব্যবহারের ক্ষেত্রে এটিই সবচেয়ে প্রভাবশালী এবং কার্যকরী অংশ। যত তাড়াতাড়ি সম্ভব প্রোফাইল তৈরি করুন।
গেমের ধরন, অ্যাসেটের সংখ্যা ও প্রকার এবং মেমরি অপ্টিমাইজেশন কৌশলের উপর নির্ভর করে অ্যান্ড্রয়েড গেমের মেমরি ব্যবহার উল্লেখযোগ্যভাবে পরিবর্তিত হতে পারে। তবে, সাধারণত টেক্সচার, মেশ, অডিও ফাইল, শেডার, অ্যানিমেশন এবং স্ক্রিপ্টগুলোই মেমরি ব্যবহারে বেশি অবদান রাখে।
নকল সম্পদ সনাক্ত করুন
প্রথম ধাপ হলো মেমরি প্রোফাইলার, বিল্ড রিপোর্ট টুল বা প্রজেক্ট অডিটর ব্যবহার করে ত্রুটিপূর্ণভাবে কনফিগার করা অ্যাসেট এবং ডুপ্লিকেট অ্যাসেট শনাক্ত করা।
টেক্সচার
আপনার গেমের ডিভাইস সাপোর্ট বিশ্লেষণ করুন এবং সঠিক টেক্সচার ফরম্যাট নির্ধারণ করুন। আপনি প্লে অ্যাসেট ডেলিভারি (Play Asset Delivery) , অ্যাড্রেসেবল (Addressable) , অথবা অ্যাসেটবান্ডেল (AssetBundle) এর মতো আরও ম্যানুয়াল প্রক্রিয়ার মাধ্যমে হাই-এন্ড এবং লো-এন্ড ডিভাইসের জন্য টেক্সচার বান্ডলগুলো ভাগ করতে পারেন।
"আপনার মোবাইল গেমের পারফরম্যান্স অপ্টিমাইজ করুন" এবং "ইউনিটি টেক্সচার ইম্পোর্ট সেটিংস অপ্টিমাইজ করা" আলোচনা পোস্টে উপলব্ধ সবচেয়ে সুপরিচিত সুপারিশগুলি অনুসরণ করুন। তারপর এই সমাধানগুলি চেষ্টা করুন:
মেমোরি ফুটপ্রিন্ট কমাতে ASTC ফরম্যাটে টেক্সচার কম্প্রেস করুন এবং 8x8-এর মতো উচ্চতর ব্লক রেট নিয়ে পরীক্ষা করুন।
যদি ETC2 ব্যবহার করা আবশ্যক হয়, তবে আপনার টেক্সচারগুলো অ্যাটলাসে প্যাক করুন। একাধিক টেক্সচারকে একটি একক টেক্সচারে রাখলে তার পাওয়ার অফ টু (POT) নিশ্চিত হয়, ড্র কল কমে যায় এবং রেন্ডারিং দ্রুততর হতে পারে।
RenderTarget টেক্সচারের ফরম্যাট ও সাইজ অপ্টিমাইজ করুন। অপ্রয়োজনীয় উচ্চ-রেজোলিউশনের টেক্সচার পরিহার করুন। মোবাইল ডিভাইসে ছোট আকারের টেক্সচার ব্যবহার করলে মেমোরি সাশ্রয় হয়।
টেক্সচার মেমরি বাঁচাতে টেক্সচার চ্যানেল প্যাকিং ব্যবহার করুন।
জাল এবং মডেল
প্রথমে মৌলিক সেটিংস (পৃষ্ঠা ২৭) পরীক্ষা করে শুরু করুন এবং এই মেশ ইম্পোর্টিং সেটিংসগুলো যাচাই করুন:
- অপ্রয়োজনীয় এবং ছোট মেশগুলোকে একত্রিত করুন।
- দৃশ্যের বস্তুগুলোর (যেমন, স্থির বা দূরবর্তী বস্তু) ভার্টেক্স সংখ্যা হ্রাস করুন।
- উচ্চ জ্যামিতিক অ্যাসেটগুলোর জন্য লেভেল অফ ডিটেইল (LOD) গ্রুপ তৈরি করুন।
উপকরণ এবং শেডার
- বিল্ড প্রক্রিয়া চলাকালীন প্রোগ্রাম্যাটিকভাবে অব্যবহৃত শেডার ভ্যারিয়েন্টগুলো অপসারণ করুন।
- শেডারের পুনরাবৃত্তি এড়াতে, ঘন ঘন ব্যবহৃত শেডার ভ্যারিয়েন্টগুলোকে উবার শেডারে একীভূত করুন।
- VRAM/RAM-এ প্রি-লোডেড শেডারগুলির বিশাল মেমরি ব্যবহারের সমস্যা সমাধানের জন্য ডাইনামিক শেডার লোডিং সক্ষম করুন। তবে, শেডার কম্পাইলেশনের কারণে ফ্রেমে বাধা সৃষ্টি হলে সতর্ক হন।
- সমস্ত ভ্যারিয়েন্ট লোড হওয়া আটকাতে ডাইনামিক শেডার লোডিং ব্যবহার করুন। আরও তথ্যের জন্য, “ Improvements to shader build times and memory usage” ব্লগ পোস্টটি দেখুন।
-
MaterialPropertyBlocksব্যবহার করে মেটেরিয়াল ইনস্ট্যান্সিং সঠিকভাবে প্রয়োগ করুন।
অডিও
প্রথমে মৌলিক সেটিংস (পৃষ্ঠা ৪১) পরীক্ষা করে শুরু করুন, এবং এই মেশ ইম্পোর্টিং সেটিংসগুলো যাচাই করুন:
- FMOD বা Wwise-এর মতো থার্ড-পার্টি অডিও ইঞ্জিন ব্যবহার করার সময় অব্যবহৃত বা অপ্রয়োজনীয়
AudioClipরেফারেন্সগুলো সরিয়ে ফেলুন। - অডিও ডেটা প্রি-লোড করুন। রানটাইম বা সিন স্টার্টআপের সময় যে ক্লিপগুলির অবিলম্বে প্রয়োজন হয় না, সেগুলির জন্য প্রি-লোড নিষ্ক্রিয় করুন। এটি সিন ইনিশিয়ালাইজেশনের সময় মেমরি ওভারহেড কমাতে সাহায্য করে।
অ্যানিমেশন
- কীফ্রেমের সংখ্যা কমাতে এবং অপ্রয়োজনীয় ডেটা বাদ দিতে ইউনিটির অ্যানিমেশন কম্প্রেশন সেটিংস অ্যাডজাস্ট করুন।
- কীফ্রেম হ্রাস: স্বয়ংক্রিয়ভাবে অপ্রয়োজনীয় কীফ্রেমগুলি সরিয়ে দেয়
- কোয়াটারনিয়ন কম্প্রেশন: মেমরি ব্যবহার কমাতে রোটেশন ডেটা সংকুচিত করে।
আপনি রিগ অথবা অ্যানিমেশন ট্যাবের অধীনে থাকা অ্যানিমেশন ইমপোর্ট সেটিংস -এ কম্প্রেশন সেটিংস সমন্বয় করতে পারেন।
বিভিন্ন অবজেক্টের জন্য অ্যানিমেশন ক্লিপ নকল করার পরিবর্তে, অ্যানিমেশন ক্লিপগুলো পুনরায় ব্যবহার করুন।
একটি অ্যানিমেটর কন্ট্রোলার পুনরায় ব্যবহার করতে এবং বিভিন্ন চরিত্রের জন্য নির্দিষ্ট ক্লিপ প্রতিস্থাপন করতে অ্যানিমেটর ওভাররাইড কন্ট্রোলার ব্যবহার করুন।
পদার্থবিদ্যা-ভিত্তিক অ্যানিমেশন বেক করুন: আপনার অ্যানিমেশনগুলো যদি পদার্থবিদ্যা-চালিত বা পদ্ধতিগত হয়, তবে রানটাইম গণনা এড়াতে সেগুলোকে অ্যানিমেশন ক্লিপে বেক করুন।
স্কেলেটন রিগ অপ্টিমাইজ করুন: জটিলতা এবং মেমরি ব্যবহার কমাতে আপনার রিগে কম বোন ব্যবহার করুন।
- ছোট বা স্থির বস্তুর ক্ষেত্রে অতিরিক্ত হাড় ব্যবহার করা পরিহার করুন।
- যদি নির্দিষ্ট কিছু বোন অ্যানিমেটেড না হয় বা সেগুলোর প্রয়োজন না থাকে, তাহলে সেগুলোকে রিগ থেকে সরিয়ে দিন।
অ্যানিমেশন ক্লিপের দৈর্ঘ্য কমান।
- অ্যানিমেশন ক্লিপগুলো ছেঁটে শুধু প্রয়োজনীয় ফ্রেমগুলো রাখুন। অব্যবহৃত বা অতিরিক্ত দীর্ঘ অ্যানিমেশন সংরক্ষণ করা থেকে বিরত থাকুন।
- পুনরাবৃত্তিমূলক নড়াচড়ার জন্য দীর্ঘ ক্লিপ তৈরি করার পরিবর্তে লুপিং অ্যানিমেশন ব্যবহার করুন।
নিশ্চিত করুন যেন শুধুমাত্র একটি অ্যানিমেশন কম্পোনেন্ট সংযুক্ত বা সক্রিয় থাকে। উদাহরণস্বরূপ, আপনি যদি Animator ব্যবহার করেন, তাহলে Legacy অ্যানিমেশন কম্পোনেন্টগুলো নিষ্ক্রিয় বা সরিয়ে ফেলুন।
অপ্রয়োজনে অ্যানিমেটর ব্যবহার করা এড়িয়ে চলুন। সাধারণ ভিএফএক্স-এর জন্য টুইনিং লাইব্রেরি ব্যবহার করুন অথবা স্ক্রিপ্টে ভিজ্যুয়াল এফেক্টটি প্রয়োগ করুন। অ্যানিমেটর সিস্টেমটি রিসোর্স-ইনটেনসিভ হতে পারে, বিশেষ করে লো-এন্ড মোবাইল ডিভাইসগুলোতে।
বিপুল সংখ্যক অ্যানিমেশন পরিচালনা করার সময় জব সিস্টেম ব্যবহার করুন, কারণ এই সিস্টেমটিকে আরও বেশি মেমোরি-সাশ্রয়ী করার জন্য সম্পূর্ণরূপে নতুন করে ডিজাইন করা হয়েছে।
দৃশ্য
যখন নতুন সিন লোড করা হয়, তখন সেগুলো ডিপেন্ডেন্সি হিসেবে অ্যাসেট নিয়ে আসে। তবে, যথাযথ অ্যাসেট লাইফসাইকেল ম্যানেজমেন্ট না থাকলে, রেফারেন্স কাউন্টারের মাধ্যমে এই ডিপেন্ডেন্সিগুলো পর্যবেক্ষণ করা হয় না। ফলে, অব্যবহৃত সিনগুলো আনলোড হয়ে যাওয়ার পরেও অ্যাসেটগুলো মেমরিতে থেকে যেতে পারে, যা মেমরি ফ্র্যাগমেন্টেশনের কারণ হয়।
- বারবার ব্যবহৃত গেমপ্লে উপাদানগুলির জন্য GameObject ইনস্ট্যান্সগুলি পুনরায় ব্যবহার করতে ইউনিটির অবজেক্ট পুলিং ব্যবহার করুন, কারণ অবজেক্ট পুলিং পুনরায় ব্যবহারের জন্য অবজেক্ট ইনস্ট্যান্সের একটি সংগ্রহ ধরে রাখতে একটি স্ট্যাক ব্যবহার করে এবং এটি থ্রেড সেফ নয়।
InstantiateএবংDestroyব্যবহার কমানো সিপিইউ পারফরম্যান্স এবং মেমরি স্থিতিশীলতা উভয়ই উন্নত করে। - সম্পদ খালাস করা:
- স্প্ল্যাশ স্ক্রিন বা লোডিং স্ক্রিনের মতো কম গুরুত্বপূর্ণ মুহূর্তগুলোতে কৌশলগতভাবে অ্যাসেটগুলো আনলোড করুন।
-
Resources.UnloadUnusedAssetsএর ঘন ঘন ব্যবহারের ফলে বড় আকারের অভ্যন্তরীণ নির্ভরতা পর্যবেক্ষণ কার্যক্রম সংঘটিত হয়, যা সিপিইউ প্রসেসিং-এ আকস্মিক বৃদ্ধি ঘটায়। - GC.MarkDependencies প্রোফাইল মার্কারে বড় ধরনের সিপিইউ স্পাইক আছে কিনা তা পরীক্ষা করুন। এটি সরিয়ে ফেলুন বা এর এক্সিকিউশন ফ্রিকোয়েন্সি কমিয়ে দিন, এবং সর্বব্যাপী
Resources.UnloadUnusedAssets()-এর উপর নির্ভর না করে Resources.UnloadAsset ব্যবহার করে ম্যানুয়ালি নির্দিষ্ট রিসোর্স আনলোড করুন।
- বারবার Resources.UnloadUnusedAssets ব্যবহার না করে সিনগুলো পুনর্গঠন করুন।
-
Addressablesজন্যResources.UnloadUnusedAssets()কল করলে অনিচ্ছাকৃতভাবে ডাইনামিক্যালি লোড করা বান্ডেল আনলোড হয়ে যেতে পারে। ডাইনামিক্যালি লোড করা অ্যাসেটগুলোর লাইফসাইকেল সাবধানে পরিচালনা করুন।
বিবিধ
দৃশ্য পরিবর্তনের কারণে সৃষ্ট খণ্ডীকরণ — যখন
Resources.UnloadUnusedAssets()পদ্ধতিটি কল করা হয়, তখন ইউনিটি নিম্নলিখিত কাজগুলো করে:- অব্যবহৃত অ্যাসেটগুলোর জন্য মেমরি খালি করে।
- অব্যবহৃত অ্যাসেটগুলির জন্য ম্যানেজড এবং নেটিভ অবজেক্ট হিপ পরীক্ষা করতে এবং সেগুলিকে আনলোড করতে একটি গার্বেজ কালেক্টরের মতো অপারেশন চালায়।
- টেক্সচার, মেশ এবং অ্যাসেট মেমরি পরিষ্কার করে, যদি কোনো সক্রিয় রেফারেন্স বিদ্যমান না থাকে।
AssetBundleবাAddressable- এই ক্ষেত্রে পরিবর্তন আনা বেশ জটিল এবং কৌশলগুলো বাস্তবায়নের জন্য দলের সম্মিলিত প্রচেষ্টা প্রয়োজন। তবে, একবার এই কৌশলগুলো আয়ত্তে আনা গেলে, এগুলো মেমোরির ব্যবহার উল্লেখযোগ্যভাবে উন্নত করে, ডাউনলোডের আকার কমায় এবং ক্লাউড খরচ হ্রাস করে। ইউনিটিতে অ্যাসেট ম্যানেজমেন্ট সম্পর্কে আরও তথ্যের জন্য,Addressablesদেখুন।কেন্দ্রীভূত শেয়ার্ড ডিপেন্ডেন্সি — শেডার, টেক্সচার এবং ফন্টের মতো শেয়ার্ড ডিপেন্ডেন্সিগুলোকে পদ্ধতিগতভাবে ডেডিকেটেড বান্ডেল বা
Addressableগ্রুপে একত্রিত করুন। এটি ডুপ্লিকেশন কমায় এবং অপ্রয়োজনীয় অ্যাসেটগুলো দক্ষতার সাথে আনলোড হওয়া নিশ্চিত করে।ডিপেন্ডেন্সি ট্র্যাকিংয়ের জন্য
Addressablesব্যবহার করুন - অ্যাড্রেসেবলস লোডিং এবং আনলোডিং সহজ করে এবং স্বয়ংক্রিয়ভাবে সেইসব ডিপেন্ডেন্সি আনলোড করতে পারে যেগুলো আর রেফারেন্স করা হয় না। গেমটির নির্দিষ্ট পরিস্থিতির উপর নির্ভর করে, কন্টেন্ট ম্যানেজমেন্ট এবং ডিপেন্ডেন্সি সমাধানের জন্যAddressables-এ স্থানান্তরিত হওয়া একটি কার্যকর সমাধান হতে পারে। অপ্রয়োজনীয় ডুপ্লিকেট বা ডিপেন্ডেন্সি শনাক্ত করতে অ্যানালাইজ টুল দিয়ে ডিপেন্ডেন্সি চেইন বিশ্লেষণ করুন। বিকল্পভাবে, আপনি যদি অ্যাসেটবান্ডেলস ব্যবহার করেন, তবে ইউনিটি ডেটা টুলস দেখুন।TypeTrees- যদি আপনার গেমেরAddressablesএবংAssetBundlesপ্লেয়ারের ব্যবহৃত ইউনিটির একই সংস্করণ দিয়ে বিল্ড ও ডেপ্লয় করা হয় এবং অন্যান্য প্লেয়ার বিল্ডের সাথে ব্যাকওয়ার্ড কম্প্যাটিবিলিটির প্রয়োজন না হয়, তাহলেTypeTreeলেখা নিষ্ক্রিয় করার কথা বিবেচনা করুন, যা বান্ডেলের আকার এবং সিরিয়ালাইজড ফাইল অবজেক্টের মেমরি ফুটপ্রিন্ট কমাতে সাহায্য করবে। লোকাল অ্যাড্রেসেবলস প্যাকেজের বিল্ড প্রসেসে ContentBuildFlags সেটিংটি পরিবর্তন করে DisableWriteTypeTree করুন ।
গার্বেজ কালেক্টর-বান্ধব কোড লিখুন
ইউনিটি অব্যবহৃত মেমরি স্বয়ংক্রিয়ভাবে শনাক্ত করে এবং খালি করার মাধ্যমে মেমরি ব্যবস্থাপনার জন্য গার্বেজ কালেকশন (GC) ব্যবহার করে। যদিও GC অপরিহার্য, তবে এটি সঠিকভাবে পরিচালিত না হলে পারফরম্যান্সের সমস্যা (যেমন, ফ্রেম রেট হঠাৎ বেড়ে যাওয়া) সৃষ্টি করতে পারে, কারণ এই প্রক্রিয়াটি মুহূর্তের জন্য গেমটিকে থামিয়ে দিতে পারে, যার ফলে পারফরম্যান্সে বাধা সৃষ্টি হয় এবং ব্যবহারকারীর অভিজ্ঞতা সর্বোত্তম হয় না।
ম্যানেজড হিপ অ্যালোকেশনের পুনরাবৃত্তি কমানোর কার্যকরী কৌশল জানতে ইউনিটি ম্যানুয়াল এবং উদাহরণের জন্য ইউনিটি পারফরম্যান্স টিউনিং বাইবেল-এর ২৭১ পৃষ্ঠা দেখুন।
গার্বেজ কালেক্টর অ্যালোকেশন হ্রাস করুন:
- LINQ, ল্যাম্বডা এবং ক্লোজার পরিহার করুন, যেগুলো হিপ মেমরি বরাদ্দ করে।
- স্ট্রিং সংযুক্ত করার পরিবর্তে পরিবর্তনযোগ্য স্ট্রিংয়ের জন্য
StringBuilderব্যবহার করুন। - কালেকশনগুলোকে পুনরায় ইনস্ট্যানশিয়েট না করে
COLLECTIONS.Clear()কল করে পুনরায় ব্যবহার করুন।
ইউনিটি গেমস প্রোফাইলিং-এর চূড়ান্ত নির্দেশিকা ই-বুকে আরও তথ্য পাওয়া যাবে।
UI ক্যানভাস আপডেটগুলি পরিচালনা করুন:
- UI এলিমেন্টের ডাইনামিক পরিবর্তন — যখন Text, Image, বা
RectTransformপ্রোপার্টির মতো UI এলিমেন্টগুলো আপডেট করা হয় (উদাহরণস্বরূপ, টেক্সটের বিষয়বস্তু পরিবর্তন করা, এলিমেন্টের আকার পরিবর্তন করা, বা পজিশন অ্যানিমেট করা), তখন ইঞ্জিন টেম্পোরারি অবজেক্টের জন্য মেমরি বরাদ্দ করতে পারে। - স্ট্রিং অ্যালোকেশন — টেক্সটের মতো UI এলিমেন্টগুলিতে প্রায়শই স্ট্রিং আপডেটের প্রয়োজন হয়, কারণ বেশিরভাগ প্রোগ্রামিং ভাষায় স্ট্রিং অপরিবর্তনীয়।
- ডার্টি ক্যানভাস — যখন ক্যানভাসের কোনো কিছু পরিবর্তিত হয় (যেমন, আকার পরিবর্তন, এলিমেন্ট সক্রিয় বা নিষ্ক্রিয় করা, অথবা লেআউট প্রোপার্টি পরিবর্তন করা), তখন সম্পূর্ণ ক্যানভাস বা এর কোনো অংশ ডার্টি হিসেবে চিহ্নিত হতে পারে এবং পুনর্নির্মিত হতে পারে। এর ফলে অস্থায়ী ডেটা স্ট্রাকচার (যেমন, মেশ ডেটা, ভার্টেক্স বাফার, বা লেআউট ক্যালকুলেশন) তৈরি হতে পারে, যা গার্বেজ জেনারেশন বাড়িয়ে দেয়।
- সম্পূর্ণ বা ঘন ঘন আপডেট — যদি ক্যানভাসে প্রচুর সংখ্যক উপাদান থাকে বা এটি ঘন ঘন আপডেট করা হয় (উদাহরণস্বরূপ, প্রতি ফ্রেমে), তবে এই পুনর্নির্মাণগুলি উল্লেখযোগ্য পরিমাণে মেমোরি ব্যবহার করতে পারে।
- UI এলিমেন্টের ডাইনামিক পরিবর্তন — যখন Text, Image, বা
একাধিক ফ্রেমে অ্যালোকেশন ক্লিনআপ ছড়িয়ে দিয়ে বড় কালেকশন স্পাইক কমাতে ইনক্রিমেন্টাল GC সক্রিয় করুন । এই অপশনটি আপনার গেমের পারফরম্যান্স এবং মেমরি ফুটপ্রিন্ট উন্নত করে কিনা তা যাচাই করতে প্রোফাইল দেখুন।
আপনার গেমে যদি একটি নিয়ন্ত্রিত পদ্ধতির প্রয়োজন হয়, তাহলে গার্বেজ কালেকশন মোডটি ম্যানুয়াল-এ সেট করুন। এরপর, লেভেল পরিবর্তনের সময় বা সক্রিয় গেমপ্লে ছাড়া অন্য কোনো মুহূর্তে গার্বেজ কালেকশন চালু করুন।
গেমের অবস্থার পরিবর্তনের জন্য (যেমন, লেভেল পরিবর্তন) ম্যানুয়ালি গার্বেজ কালেকশন GC.Collect() কল করুন।
সহজ কোড অনুশীলন থেকে শুরু করে এবং প্রয়োজনে বড় অ্যারের জন্য নেটিভ অ্যারে বা অন্যান্য নেটিভ কন্টেইনার ব্যবহার করে অ্যারে অপ্টিমাইজ করুন।
ইউনিটি মেমোরি প্রোফাইলারের মতো টুল ব্যবহার করে ম্যানেজড অবজেক্টগুলো মনিটর করুন, যাতে ধ্বংসের পরেও টিকে থাকা আনম্যানেজড অবজেক্ট রেফারেন্সগুলো ট্র্যাক করা যায়।
স্বয়ংক্রিয় পদ্ধতির জন্য পারফরম্যান্স রিপোর্টিং টুলে জমা দিতে একটি প্রোফাইলার মার্কার ব্যবহার করুন।
মেমরি লিক এবং ফ্র্যাগমেন্টেশন এড়িয়ে চলুন
মেমরি লিক
C# কোডে, কোনো ইউনিটি অবজেক্ট ডেস্ট্রয় (destroy) হয়ে যাওয়ার পরেও যদি সেটির একটি রেফারেন্স থেকে যায়, তবে ম্যানেজড র্যাপার অবজেক্টটি (managed wrapper object), যা ম্যানেজড শেল (Managed Shell) নামে পরিচিত, মেমোরিতে থেকে যায়। রেফারেন্সটির সাথে যুক্ত নেটিভ মেমোরি (native memory) তখন মুক্ত হয় যখন সিন (scene) আনলোড (unload) করা হয় অথবা যখন মেমোরিটি যে গেমঅবজেক্টের (GameObject) সাথে সংযুক্ত, সেটি বা তার যেকোনো প্যারেন্ট অবজেক্টকে Destroy() মেথডের মাধ্যমে ডেস্ট্রয় করা হয়। তবে, যদি সিন বা গেমঅবজেক্টের অন্যান্য রেফারেন্সগুলো ক্লিয়ার (clear) করা না হয়, তাহলে ম্যানেজড মেমোরিটি একটি লিকড শেল অবজেক্ট (Leaked Shell Object) হিসেবে থেকে যেতে পারে । ম্যানেজড শেল অবজেক্ট সম্পর্কে আরও বিস্তারিত জানতে, ম্যানেজড শেল অবজেক্ট ম্যানুয়ালটি দেখুন।
এছাড়াও, ইভেন্ট সাবস্ক্রিপশন, ল্যাম্বডা ও ক্লোজার, স্ট্রিং কনক্যাটেনেশন এবং পুলড অবজেক্টের অনুপযুক্ত ব্যবস্থাপনার কারণেও মেমোরি লিক হতে পারে।
- শুরু করার জন্য, ইউনিটি মেমরি স্ন্যাপশটগুলো সঠিকভাবে তুলনা করতে ‘ফাইন্ড মেমরি লিকস’ দেখুন।
- ইভেন্ট সাবস্ক্রিপশন এবং মেমরি লিক পরীক্ষা করুন। যদি অবজেক্টগুলো ইভেন্টে সাবস্ক্রাইব করে (উদাহরণস্বরূপ, ডেলিগেট বা UnityEvents-এর মাধ্যমে) কিন্তু ধ্বংস হওয়ার আগে সঠিকভাবে আনসাবস্ক্রাইব না করে, তাহলে ইভেন্ট ম্যানেজার বা পাবলিশার সেই অবজেক্টগুলোর রেফারেন্স ধরে রাখতে পারে। এর ফলে অবজেক্টগুলো গার্বেজ কালেকশন থেকে বঞ্চিত হয়, যা মেমরি লিকের কারণ হতে পারে।
- গ্লোবাল বা সিঙ্গেলটন ক্লাসের সেইসব ইভেন্ট মনিটর করুন যেগুলো অবজেক্ট ধ্বংসের সময় আনরেজিস্টার করা হয় না। উদাহরণস্বরূপ, অবজেক্ট ডেস্ট্রাক্টরে ডেলিগেট আনসাবস্ক্রাইব বা আনহুক করুন।
- নিশ্চিত করুন যে পুল করা অবজেক্টগুলির ধ্বংসসাধন টেক্সট মেশ কম্পোনেন্ট , টেক্সচার এবং প্যারেন্ট গেমঅবজেক্টগুলির রেফারেন্সকে সম্পূর্ণরূপে বাতিল করে দেয়।
- মনে রাখবেন যে, ইউনিটি মেমোরি প্রোফাইলার স্ন্যাপশটগুলো তুলনা করার সময় কোনো সুস্পষ্ট কারণ ছাড়াই মেমোরি ব্যবহারে পার্থক্য লক্ষ্য করলে, সেই পার্থক্যটি গ্রাফিক্স ড্রাইভার বা স্বয়ং অপারেটিং সিস্টেমের কারণেও হতে পারে।
স্মৃতি খণ্ডন
যখন অনেকগুলো ছোট ছোট অ্যালোকেশন এলোমেলো ক্রমে মুক্ত করা হয়, তখন মেমোরি ফ্র্যাগমেন্টেশন ঘটে। হিপ অ্যালোকেশনগুলো ক্রমানুসারে করা হয়, যার অর্থ হলো আগের চাঙ্কে জায়গা ফুরিয়ে গেলে নতুন মেমোরি চাঙ্ক তৈরি করা হয়। ফলস্বরূপ, নতুন অবজেক্টগুলো পুরোনো চাঙ্কের খালি জায়গাগুলো পূরণ করে না, যা ফ্র্যাগমেন্টেশনের কারণ হয়। এছাড়াও, বড় আকারের অস্থায়ী অ্যালোকেশনগুলো একটি গেম সেশনের পুরো সময় জুড়ে স্থায়ী ফ্র্যাগমেন্টেশনের কারণ হতে পারে।
এই সমস্যাটি বিশেষভাবে জটিল হয়ে ওঠে যখন দীর্ঘমেয়াদী বরাদ্দের কাছাকাছি স্বল্পমেয়াদী বড় বরাদ্দ করা হয়।
জীবনকালের উপর ভিত্তি করে গ্রুপভিত্তিক বরাদ্দ করা উচিত; আদর্শগতভাবে, দীর্ঘমেয়াদী বরাদ্দগুলো অ্যাপ্লিকেশনটির জীবনচক্রের শুরুতেই একসাথে করা উচিত।
পর্যবেক্ষক এবং ইভেন্ট ম্যানেজার
- মেমরি লিকস বিভাগে উল্লিখিত সমস্যা ছাড়াও, সময়ের সাথে সাথে মেমরি লিকস এমন অবজেক্টের জন্য বরাদ্দকৃত অব্যবহৃত মেমরি রেখে দেয় যা আর ব্যবহৃত হচ্ছে না, যার ফলে ফ্র্যাগমেন্টেশন হতে পারে।
- নিশ্চিত করুন যে পুল করা অবজেক্টগুলির ধ্বংস টেক্সট মেশ কম্পোনেন্ট , টেক্সচার এবং প্যারেন্ট
GameObjectsরেফারেন্সকে সম্পূর্ণরূপে বাতিল করে দেয়। - ইভেন্ট ম্যানেজাররা প্রায়শই ইভেন্ট সাবস্ক্রিপশন পরিচালনা করার জন্য তালিকা বা ডিকশনারি তৈরি ও সংরক্ষণ করে। রানটাইমের সময় যদি এগুলি গতিশীলভাবে বাড়ে ও কমে, তবে ঘন ঘন অ্যালোকেশন এবং ডিঅ্যালোকেশনের কারণে এগুলি মেমরি ফ্র্যাগমেন্টেশনের কারণ হতে পারে।
কোড
- কো-রুটিন মাঝে মাঝে মেমরি বরাদ্দ করে, যা প্রতিবার নতুন করে IEnumerator ডিক্লেয়ার করার পরিবর্তে এর রিটার্ন স্টেটমেন্টটি ক্যাশ করে সহজেই এড়ানো যায়।
-
UnityEngine.Objectগোস্ট রেফারেন্স রাখা এড়াতে পুল করা অবজেক্টগুলোর লাইফসাইকেল স্টেট ক্রমাগত পর্যবেক্ষণ করুন।
সম্পদ
- বহুভাষিক ক্ষেত্রে সমস্ত ফন্ট আগে থেকে লোড করা এড়াতে টেক্সট-ভিত্তিক গেমের জন্য ডাইনামিক ফলব্যাক সিস্টেম ব্যবহার করুন।
- অ্যাসেটগুলোকে (যেমন, টেক্সচার এবং পার্টিকেল) তাদের ধরন এবং প্রত্যাশিত জীবনচক্র অনুযায়ী একসাথে সাজিয়ে রাখুন।
- অপ্রয়োজনীয় UI ইমেজ এবং স্ট্যাটিক মেশের মতো নিষ্ক্রিয় লাইফসাইকেল অ্যাট্রিবিউটযুক্ত অ্যাসেটগুলোকে সংক্ষেপ করুন।
জীবনব্যাপী বরাদ্দ
- সুসংহত বরাদ্দ নিশ্চিত করতে অ্যাপ্লিকেশন জীবনচক্রের শুরুতেই দীর্ঘমেয়াদী সম্পদ বরাদ্দ করুন।
- মেমরি-নিবিড় বা ক্ষণস্থায়ী ডেটা স্ট্রাকচারের (যেমন, ফিজিক্স ক্লাস্টার) জন্য নেটিভ কালেকশন বা কাস্টম অ্যালোকেটর ব্যবহার করুন।
কোড-সম্পর্কিত এবং এক্সিকিউটেবল মেমরি অ্যাকশন
গেম এক্সিকিউটেবল এবং প্লাগইনগুলোও মেমরি ব্যবহারকে প্রভাবিত করে।
IL2CPP মেটাডেটা
IL2CPP বিল্ড টাইমে প্রতিটি টাইপের (যেমন, ক্লাস, জেনেরিক এবং ডেলিগেট) জন্য মেটাডেটা তৈরি করে, যা পরবর্তীতে রানটাইমে রিফ্লেকশন, টাইপ চেকিং এবং অন্যান্য রানটাইম-নির্দিষ্ট অপারেশনের জন্য ব্যবহৃত হয়। এই মেটাডেটা মেমরিতে সংরক্ষিত থাকে এবং অ্যাপ্লিকেশনটির মোট মেমরি ফুটপ্রিন্টে উল্লেখযোগ্যভাবে অবদান রাখতে পারে। IL2CPP-এর মেটাডেটা ক্যাশে ইনিশিয়ালাইজেশন এবং লোডিং টাইমে একটি গুরুত্বপূর্ণ ভূমিকা রাখে। এছাড়াও, IL2CPP কিছু নির্দিষ্ট মেটাডেটা এলিমেন্টের (যেমন, জেনেরিক টাইপ বা সিরিয়ালাইজড তথ্য) ডুপ্লিকেট অপসারণ করে না, যার ফলে মেমরির ব্যবহার অস্বাভাবিকভাবে বেড়ে যেতে পারে। প্রজেক্টে পুনরাবৃত্তিমূলক বা অপ্রয়োজনীয় টাইপ ব্যবহারের কারণে এই সমস্যা আরও প্রকট হয়।
IL2CPP মেটাডেটা নিম্নলিখিত উপায়ে কমানো যেতে পারে:
- রিফ্লেকশন এপিআই (reflection API) ব্যবহার পরিহার করুন, কারণ এগুলো IL2CPP মেটাডেটা অ্যালোকেশনের একটি উল্লেখযোগ্য কারণ হতে পারে।
- অন্তর্নির্মিত প্যাকেজ নিষ্ক্রিয় করা
- ইউনিটি ২০২২-এর সম্পূর্ণ জেনেরিক শেয়ারিং প্রয়োগ করা হচ্ছে, যা জেনেরিকের কারণে সৃষ্ট ওভারহেড কমাতে সাহায্য করবে। তবে, অ্যালোকেশন আরও কমাতে হলে জেনেরিকের ব্যবহার কমিয়ে দিন।
কোড স্ট্রিপিং
বিল্ডের আকার কমানোর পাশাপাশি, কোড স্ট্রিপিং মেমরি ব্যবহারও হ্রাস করে। IL2CPP স্ক্রিপ্টিং ব্যাকএন্ডের বিপরীতে বিল্ড করার সময়, ম্যানেজড বাইটকোড স্ট্রিপিং (যা ডিফল্টরূপে সক্রিয় থাকে) ম্যানেজড অ্যাসেম্বলিগুলো থেকে অব্যবহৃত কোড সরিয়ে দেয়। এই প্রক্রিয়াটি প্রথমে রুট অ্যাসেম্বলিগুলো সংজ্ঞায়িত করে এবং তারপর স্ট্যাটিক কোড অ্যানালাইসিস ব্যবহার করে নির্ধারণ করে যে সেই রুট অ্যাসেম্বলিগুলো অন্য কোন ম্যানেজড কোড ব্যবহার করে। যে কোডগুলো অ্যাক্সেসযোগ্য নয়, সেগুলো সরিয়ে ফেলা হয়। ম্যানেজড কোড স্ট্রিপিং সম্পর্কে আরও তথ্যের জন্য, "TTales from the optimization trenches: Better managed code stripping with Unity 2020 LTS" ব্লগ পোস্ট এবং ম্যানেজড কোড স্ট্রিপিং ডকুমেন্টেশন দেখুন।
স্থানীয় বরাদ্দকারীরা
মেমরি অ্যালোকেটরগুলোকে সূক্ষ্মভাবে সমন্বয় করতে নেটিভ মেমরি অ্যালোকেটর নিয়ে পরীক্ষা-নিরীক্ষা করুন। গেমটিতে মেমরি কম থাকলে, ছোট মেমরি ব্লক ব্যবহার করুন, এমনকি এর ফলে অ্যালোকেটরগুলো ধীরগতির হলেও। আরও জানতে ডাইনামিক হিপ অ্যালোকেটর উদাহরণটি দেখুন।
নেটিভ প্লাগইন এবং এসডিকে পরিচালনা করুন
সমস্যা সৃষ্টিকারী প্লাগইনটি খুঁজুন — প্রতিটি প্লাগইন সরিয়ে ফেলুন এবং গেম মেমোরির স্ন্যাপশটগুলো তুলনা করুন। এর জন্য স্ক্রিপ্টিং ডিফাইন সিম্বলস ব্যবহার করে কোডের অনেক কার্যকারিতা নিষ্ক্রিয় করতে হবে এবং ইন্টারফেসের সাহায্যে অত্যন্ত কাপলড ক্লাসগুলোকে রিফ্যাক্টর করতে হবে। আপনার গেমকে খেলার অযোগ্য না করেই এক্সটার্নাল ডিপেন্ডেন্সি নিষ্ক্রিয় করার প্রক্রিয়াটিকে সহজ করতে গেম প্রোগ্রামিং প্যাটার্নের সাহায্যে আপনার কোডের মান উন্নত করুন।
প্লাগইন বা এসডিকে লেখকের সাথে যোগাযোগ করুন — বেশিরভাগ প্লাগইনই ওপেন সোর্স নয়।
প্লাগইনের মেমরি ব্যবহার পুনরায় তৈরি করুন — আপনি একটি সাধারণ প্লাগইন লিখতে পারেন (রেফারেন্স হিসেবে এই ইউনিটি প্লাগইনটি ব্যবহার করুন) যা মেমরি অ্যালোকেশন করে। অ্যান্ড্রয়েড স্টুডিও ব্যবহার করে মেমরি স্ন্যাপশটগুলো পরীক্ষা করুন (যেহেতু ইউনিটি এই অ্যালোকেশনগুলো ট্র্যাক করে না) অথবা একই প্রজেক্টে
MemoryInfoক্লাস এবংRuntime.totalMemory()মেথডটি কল করুন।
একটি ইউনিটি প্লাগইন জাভা এবং নেটিভ মেমরি বরাদ্দ করে; এটি করার পদ্ধতি নিচে দেওয়া হলো:
জাভা
byte[] largeObject = new byte[1024 * 1024 * megaBytes];
list.add(largeObject);
স্থানীয়
char* buffer = new char[megabytes * 1024 * 1024];
// Random data to fill the buffer
for (int i = 1; i < megabytes * 1024 * 1024; ++i) {
buffer[i] = 'A' + (i % 26); // Fill with letters A-Z
}