اشکال زدایی LMK ها

حل LMK ها در بازی Unity شما یک فرآیند سیستماتیک است:

شکل 1. مراحل حل موارد کم حافظه (LMK) در بازی های یونیتی.

یک عکس فوری از حافظه دریافت کنید

از Unity Profiler برای دریافت یک عکس فوری از حافظه مدیریت شده توسط Unity استفاده کنید. شکل 2 لایه های مدیریت حافظه را نشان می دهد که Unity برای مدیریت حافظه در بازی شما استفاده می کند.

شکل 2. نمای کلی مدیریت حافظه Unity.

حافظه مدیریت شده

مدیریت حافظه یونیتی یک لایه حافظه کنترل شده را پیاده سازی می کند که از یک پشته مدیریت شده و یک جمع کننده زباله برای تخصیص و تخصیص خودکار حافظه استفاده می کند. سیستم حافظه مدیریت شده یک محیط برنامه نویسی C# بر اساس Mono یا IL2CPP است. مزیت سیستم حافظه مدیریت شده این است که از یک جمع کننده زباله برای آزادسازی خودکار تخصیص حافظه استفاده می کند.

حافظه مدیریت نشده سی شارپ

لایه حافظه C# مدیریت نشده دسترسی به لایه حافظه بومی را فراهم می کند و کنترل دقیقی را بر تخصیص حافظه در حین استفاده از کد C# امکان پذیر می کند. این لایه مدیریت حافظه از طریق فضای نام Unity.Collections و با توابعی مانند UnsafeUtility.Malloc و UnsafeUtility.Free قابل دسترسی است.

حافظه بومی

هسته داخلی C/C++ Unity از یک سیستم حافظه بومی برای مدیریت صحنه‌ها، دارایی‌ها، APIهای گرافیکی، درایورها، زیرسیستم‌ها و بافرهای پلاگین استفاده می‌کند. در حالی که دسترسی مستقیم محدود است، می توانید با خیال راحت داده ها را با C# API Unity دستکاری کنید و از کد بومی کارآمد بهره مند شوید. حافظه اصلی به ندرت به تعامل مستقیم نیاز دارد، اما می‌توانید با استفاده از Profiler تأثیر حافظه بومی بر عملکرد را بررسی کنید و تنظیمات را برای بهینه‌سازی عملکرد تنظیم کنید.

همانطور که در شکل 3 نشان داده شده است، حافظه بین C# و کد اصلی به اشتراک گذاشته نمی شود. داده های مورد نیاز C# هر بار که نیاز باشد در فضای حافظه مدیریت شده تخصیص داده می شود.

به عنوان مثال، برای اینکه کد بازی مدیریت شده (C#) به داده های حافظه بومی موتور دسترسی پیدا کند، یک فراخوانی به GameObject.transform یک تماس بومی برای دسترسی به داده های حافظه در ناحیه اصلی ایجاد می کند و سپس مقادیر را با استفاده از Bindings به C# برمی گرداند. اتصالات، قراردادهای فراخوانی مناسب را برای هر پلتفرم تضمین می‌کنند و به طور خودکار انواع مدیریت‌شده را به معادل‌های اصلی آن‌ها مدیریت می‌کنند.

این تنها بار اول اتفاق می افتد، زیرا پوسته مدیریت شده برای دسترسی به ویژگی تبدیل در کد اصلی حفظ می شود. کش کردن ویژگی transform می تواند تعداد تماس های رفت و برگشتی بین کد مدیریت شده و کد بومی را کاهش دهد، اما سودمندی کش کردن به تعداد دفعات استفاده از ویژگی بستگی دارد. همچنین توجه داشته باشید که Unity هنگام دسترسی به این API ها، بخش هایی از حافظه بومی را در حافظه مدیریت شده کپی نمی کند.

شکل 3. دسترسی به حافظه بومی از کد مدیریت شده C#.

برای کسب اطلاعات بیشتر، به مقدمه Memory in Unity مراجعه کنید.

علاوه بر این، تعیین بودجه حافظه برای اجرای روان بازی شما بسیار مهم است و پیاده سازی یک سیستم تجزیه و تحلیل مصرف حافظه یا گزارش گیری تضمین می کند که هر نسخه جدید از بودجه حافظه تجاوز نمی کند. ادغام تست‌های حالت Play با یکپارچه‌سازی پیوسته (CI) برای تأیید مصرف حافظه در بخش‌های خاص بازی، استراتژی دیگری برای به دست آوردن بینش بهتر است.

مدیریت دارایی ها

این تاثیرگذارترین و کاربردی ترین بخش مصرف حافظه است. هرچه زودتر پروفایل کنید

میزان استفاده از حافظه در بازی های اندروید بسته به نوع بازی، تعداد و انواع دارایی ها و استراتژی های بهینه سازی حافظه می تواند به میزان قابل توجهی متفاوت باشد. با این حال، مشارکت‌کنندگان رایج در استفاده از حافظه معمولاً شامل بافت‌ها، مش‌ها، فایل‌های صوتی، سایه‌زن‌ها، انیمیشن‌ها و اسکریپت‌ها هستند.

شناسایی دارایی های تکراری

اولین گام این است که دارایی های پیکربندی شده ضعیف و دارایی های تکراری را با استفاده از نمایه ساز حافظه، ابزار گزارش ساخت یا Project Auditor شناسایی کنید.

بافت ها

پشتیبانی دستگاه بازی خود را تجزیه و تحلیل کنید و فرمت بافت صحیح را تعیین کنید. می‌توانید با استفاده از Play Asset Delivery ، Addressable یا فرآیند دستی‌تر با AssetBundle ، بسته‌های بافت را برای دستگاه‌های سطح بالا و پایین‌رده تقسیم کنید.

معروف‌ترین توصیه‌های موجود در بهینه‌سازی عملکرد بازی موبایلی و در پست بحث Optimizing Unity Texture Import Settings را دنبال کنید. سپس این راه حل ها را امتحان کنید:

  • بافت ها را با فرمت های ASTC فشرده کنید تا ردپای حافظه کاهش یابد و با نرخ بلوک بالاتر مانند 8x8 آزمایش کنید.

    اگر استفاده از ETC2 مورد نیاز است، بافت های خود را در اطلس بسته بندی کنید. قرار دادن چندین بافت در یک بافت واحد، قدرت دو (POT) آن را تضمین می‌کند، می‌تواند تماس‌های قرعه‌کشی را کاهش دهد و سرعت رندر را افزایش دهد.

  • قالب و اندازه بافت RenderTarget را بهینه کنید. از بافت های غیر ضروری با وضوح بالا اجتناب کنید. استفاده از بافت های کوچکتر در دستگاه های تلفن همراه باعث صرفه جویی در حافظه می شود.

  • از بسته بندی کانال بافت برای ذخیره حافظه بافت استفاده کنید.

مش ها و مدل ها

با بررسی تنظیمات اساسی (صفحه 27) شروع کنید و این تنظیمات وارد کردن مش را تأیید کنید:

  • مش های اضافی و کوچکتر را ادغام کنید.
  • تعداد راس اشیاء را در صحنه ها کاهش دهید (مثلاً اشیاء ثابت یا دور).
  • گروه های سطح جزئیات (LOD) را برای دارایی های با هندسه بالا ایجاد کنید.

مواد و شیدر

  • در طول فرآیند ساخت، انواع سایه زن استفاده نشده را به صورت برنامه ریزی شده حذف کنید.
  • برای جلوگیری از تکرار شیدر، انواع سایه زن پرکاربرد را در سایه زن های uber ادغام کنید.
  • بارگذاری سایه زن پویا را فعال کنید تا به حافظه بزرگ سایه زن های از پیش بارگذاری شده در VRAM/RAM رسیدگی شود. با این حال، اگر کامپایل سایه بان باعث سکسکه فریم می شود، توجه کنید.
  • از بارگذاری سایه زن پویا برای جلوگیری از بارگیری همه انواع استفاده کنید. برای اطلاعات بیشتر، به پست وبلاگ بهبود زمان‌های ساخت سایه‌زن و استفاده از حافظه مراجعه کنید.
  • با استفاده از MaterialPropertyBlocks از نمونه‌سازی مواد به درستی استفاده کنید.

صوتی

با بررسی تنظیمات اساسی (صفحه 41) شروع کنید و این تنظیمات وارد کردن مش را تأیید کنید:

  • هنگام استفاده از موتورهای صوتی شخص ثالث مانند FMOD یا Wwise، مراجع AudioClip استفاده نشده یا اضافی را حذف کنید.
  • داده های صوتی را از قبل بارگیری کنید. پیش‌بارگذاری کلیپ‌هایی را که در زمان اجرا یا راه‌اندازی صحنه بلافاصله مورد نیاز نیستند، غیرفعال کنید. این به کاهش سربار حافظه در هنگام اولیه سازی صحنه کمک می کند.

انیمیشن ها

  • تنظیمات فشرده سازی انیمیشن Unity را برای به حداقل رساندن تعداد فریم های کلیدی و حذف داده های اضافی تنظیم کنید.
    • کاهش فریم های کلیدی: به طور خودکار فریم های کلیدی غیر ضروری را حذف می کند
    • فشرده سازی کواترنیون: داده های چرخشی را فشرده می کند تا مصرف حافظه را کاهش دهد

می‌توانید تنظیمات فشرده‌سازی را در تنظیمات واردات انیمیشن در تب Rig یا Animation تنظیم کنید.

  • استفاده مجدد از کلیپ های انیمیشن به جای کپی کردن کلیپ های انیمیشن برای اشیاء مختلف.

    از Animator Override Controllers برای استفاده مجدد از Animator Controller و جایگزینی کلیپ های خاص برای شخصیت های مختلف استفاده کنید.

  • ساخت انیمیشن‌های مبتنی بر فیزیک: اگر انیمیشن‌های شما مبتنی بر فیزیک یا رویه‌ای هستند، آن‌ها را در کلیپ‌های انیمیشن بپزید تا از محاسبات زمان اجرا جلوگیری کنید.

  • بهینه سازی دکل اسکلت: از استخوان های کمتری در دکل خود استفاده کنید تا پیچیدگی و مصرف حافظه را کاهش دهید.

    • از استخوان بیش از حد برای اجسام کوچک یا ساکن خودداری کنید.
    • اگر استخوان های خاصی متحرک یا مورد نیاز نیستند، آنها را از دکل خارج کنید.
  • کاهش طول کلیپ انیمیشن

    • کلیپ های انیمیشن را برش دهید تا فقط فریم های لازم را شامل شود. از ذخیره انیمیشن های استفاده نشده یا بیش از حد طولانی خودداری کنید.
    • به جای ایجاد کلیپ های طولانی برای حرکات مکرر از انیمیشن های حلقه ای استفاده کنید.
  • مطمئن شوید که فقط یک جزء انیمیشن متصل یا فعال شده است. برای مثال، اگر از Animator استفاده می‌کنید، مؤلفه‌های انیمیشن قدیمی را غیرفعال یا حذف کنید.

  • در صورت غیرضروری از Animator استفاده نکنید. برای VFX ساده، از کتابخانه های tweening استفاده کنید یا جلوه بصری را در یک اسکریپت پیاده سازی کنید. سیستم انیماتور می تواند منابع فشرده ای داشته باشد، به ویژه در دستگاه های تلفن همراه ارزان قیمت.

  • هنگام کار با تعداد زیادی انیمیشن از Job System برای انیمیشن ها استفاده کنید، زیرا این سیستم به طور کامل برای کارآمدتر بودن حافظه طراحی شده است.

صحنه ها

هنگامی که صحنه های جدید بارگذاری می شوند، دارایی ها را به عنوان وابستگی وارد می کنند. با این حال، بدون مدیریت چرخه عمر دارایی مناسب، این وابستگی ها توسط شمارنده های مرجع نظارت نمی شوند. در نتیجه، ممکن است دارایی‌ها در حافظه باقی بمانند، حتی پس از تخلیه صحنه‌های استفاده نشده و باعث تکه تکه شدن حافظه شوند.

  • از Unity's Object Pooling برای استفاده مجدد از نمونه های GameObject برای عناصر بازی تکرارشونده استفاده کنید، زیرا ادغام اشیا از یک پشته برای نگهداری مجموعه ای از نمونه های اشیاء برای استفاده مجدد استفاده می کند و در رشته ای امن نیست. به حداقل رساندن Instantiate و Destroy هم عملکرد CPU و هم ثبات حافظه را بهبود می بخشد.
  • تخلیه دارایی:
    • دارایی‌ها را به‌صورت استراتژیک در لحظات بحرانی‌تر، مانند صفحه‌های آب پاش یا صفحه‌های بارگیری، بارگیری کنید.
    • استفاده مکرر از Resources.UnloadUnusedAssets به دلیل عملیات نظارت بر وابستگی داخلی بزرگ باعث افزایش در پردازش CPU می شود.
    • در نشانگر نمایه GC.MarkDependencies ، جهش‌های بزرگ CPU را بررسی کنید. فرکانس اجرای آن را حذف یا کاهش دهید، و به جای استفاده از Resources.UnloadAsset به جای اتکا به Resources.UnloadUnusedAssets() منابع خاصی را به صورت دستی بارگیری کنید.
  • به جای استفاده مداوم از منابع، صحنه ها را بازسازی کنید. UnloadUnusedAssets.
  • فراخوانی Resources.UnloadUnusedAssets() برای Addressables می تواند ناخواسته بسته های بارگذاری شده پویا را بارگیری کند. چرخه عمر دارایی های بارگذاری شده پویا را با دقت مدیریت کنید.

متفرقه

  • تکه تکه شدن ناشی از انتقال صحنه - هنگامی که متد Resources.UnloadUnusedAssets() فراخوانی می شود، Unity کارهای زیر را انجام می دهد:

    • حافظه را برای دارایی هایی که دیگر استفاده نمی شوند آزاد می کند
    • یک عملیات جمع‌آوری زباله را اجرا می‌کند تا پشته شی مدیریت‌شده و بومی را برای دارایی‌های استفاده نشده بررسی کند و آنها را تخلیه کند.
    • بافت، مش و حافظه دارایی را پاک می کند به شرطی که هیچ مرجع فعالی وجود نداشته باشد
  • AssetBundle یا Addressable - ایجاد تغییرات در این زمینه پیچیده است و نیاز به تلاش جمعی از تیم برای اجرای استراتژی ها دارد. با این حال، هنگامی که این استراتژی ها تسلط یافتند، استفاده از حافظه را به میزان قابل توجهی بهبود می بخشند، حجم دانلود را کاهش می دهند و هزینه های ابری را کاهش می دهند. برای اطلاعات بیشتر در مورد مدیریت دارایی در یونیتی با، Addressables ببینید.

  • وابستگی‌های مشترک متمرکز و mdash: وابستگی‌های مشترک، مانند سایه‌زن‌ها، بافت‌ها، و فونت‌ها را به‌طور سیستماتیک در بسته‌های اختصاصی یا گروه‌های Addressable گروه‌بندی کنید. این کار تکراری را کاهش می دهد و تضمین می کند که دارایی های غیر ضروری به طور موثر تخلیه می شوند.

  • Addressables برای ردیابی وابستگی استفاده کنید - Addressable ها بارگیری و تخلیه را ساده می کنند می توانند به طور خودکار وابستگی هایی را که دیگر به آنها ارجاع داده نمی شود، بارگیری کنند. بسته به مورد خاص بازی، انتقال به Addressables برای مدیریت محتوا و تفکیک وابستگی ممکن است یک راه حل مناسب باشد. زنجیره های وابستگی را با ابزار Analyze تجزیه و تحلیل کنید تا موارد تکراری یا وابستگی های غیر ضروری را شناسایی کنید. یا اگر از AssetBundles استفاده می کنید، به Unity Data Tools مراجعه کنید.

  • TypeTrees - اگر Addressables و AssetBundles بازی شما با استفاده از همان نسخه Unity با پلیر ساخته و مستقر شده اند و نیازی به سازگاری با نسخه های دیگر پلیر ندارند، نوشتن TypeTree را غیرفعال کنید، که باید اندازه باندل و ردپای حافظه اشیاء فایل سریالی را کاهش دهد. فرآیند ساخت را در تنظیمات بسته Addressables محلی ContentBuildFlags به DisableWriteTypeTree تغییر دهید.

کد زباله گرد پسند بنویسید

Unity از جمع‌آوری زباله (GC) برای مدیریت حافظه با شناسایی خودکار و آزاد کردن حافظه بلااستفاده استفاده می‌کند. در حالی که GC ضروری است، اگر به درستی مدیریت نشود، می‌تواند باعث مشکلات عملکرد (به عنوان مثال، افزایش نرخ فریم) شود، زیرا این فرآیند می‌تواند به‌طور لحظه‌ای بازی را متوقف کند و منجر به سکته‌های عملکردی و تجربه کاربری کمتر از حد مطلوب شود.

برای تکنیک های مفید در مورد کاهش فراوانی تخصیص هیپ مدیریت شده و برای مثال به UnityPerformanceTuningBible ، صفحه 271 به دفترچه راهنمای Unity مراجعه کنید.

  • کاهش تخصیص زباله جمع کن:

    • از LINQ، lambdas و closures که حافظه پشته را تخصیص می دهند خودداری کنید.
    • از StringBuilder برای رشته های قابل تغییر به جای الحاق رشته ها استفاده کنید.
    • استفاده مجدد از مجموعه ها با فراخوانی COLLECTIONS.Clear() به جای نمونه سازی مجدد آنها.

    اطلاعات بیشتر در کتاب الکترونیکی Ultimate Guide to Profileng Unity games موجود است.

  • مدیریت به‌روزرسانی‌های بوم رابط کاربری:

    • تغییرات پویا در عناصر UI - هنگامی که عناصر رابط کاربری مانند Text، Image یا RectTransform به روز می شوند (به عنوان مثال، تغییر محتوای متن، تغییر اندازه عناصر، یا متحرک کردن موقعیت)، موتور ممکن است حافظه را برای اشیاء موقت اختصاص دهد.
    • تخصیص رشته ها - عناصر رابط کاربری مانند Text اغلب به به روز رسانی رشته نیاز دارند، زیرا رشته ها در اکثر زبان های برنامه نویسی تغییر ناپذیر هستند.
    • بوم کثیف - وقتی چیزی روی بوم تغییر می‌کند (مثلاً تغییر اندازه، فعال کردن و غیرفعال کردن عناصر، یا اصلاح ویژگی‌های طرح‌بندی)، ممکن است کل بوم یا بخشی از آن به‌عنوان کثیف علامت‌گذاری شود و دوباره ساخته شود. این می‌تواند باعث ایجاد ساختارهای داده موقت (به عنوان مثال، داده‌های مش، بافرهای رأس یا محاسبات طرح‌بندی) شود که به تولید زباله می‌افزاید.
    • به‌روزرسانی‌های کامل یا مکرر - اگر بوم دارای تعداد زیادی عناصر است یا اغلب به‌روزرسانی می‌شود (مثلاً هر فریم)، این بازسازی‌ها می‌تواند منجر به کاهش قابل توجه حافظه شود.
  • GC افزایشی را فعال کنید تا با گسترش پاکسازی‌های تخصیص در فریم‌های متعدد، نوک‌های مجموعه بزرگ را کاهش دهید. برای بررسی اینکه آیا این گزینه عملکرد و حافظه بازی شما را بهبود می‌بخشد یا خیر، نمایه کنید.

  • اگر بازی شما به یک رویکرد کنترل شده نیاز دارد، حالت جمع آوری زباله را روی دستی تنظیم کنید. سپس، در یک تغییر سطح یا در لحظه ای دیگر بدون گیم پلی فعال، با مجموعه زباله تماس بگیرید.

  • فراخوانی دستی جمع آوری زباله GC.Collect() برای انتقال حالت بازی (مثلاً تغییر سطح).

  • آرایه‌ها را با استفاده از روش‌های کد ساده و در صورت لزوم با استفاده از آرایه‌های بومی یا سایر کانتینرهای بومی برای آرایه‌های بزرگ بهینه کنید.

  • اشیای مدیریت شده را با استفاده از ابزارهایی مانند Unity Memory Profiler برای ردیابی ارجاعات اشیای مدیریت نشده که پس از تخریب باقی می مانند، نظارت کنید.

    از یک نشانگر نمایه برای ارسال به ابزار گزارش عملکرد برای یک رویکرد خودکار استفاده کنید.

از نشت حافظه و تکه تکه شدن خودداری کنید

نشت حافظه

در کد سی شارپ، زمانی که پس از از بین رفتن شیء، ارجاع به یک شیء Unity وجود داشته باشد، شیء پوشاننده مدیریت شده، که به عنوان پوسته مدیریت شده شناخته می شود، در حافظه باقی می ماند. حافظه بومی مرتبط با مرجع هنگامی که صحنه تخلیه می شود یا زمانی که GameObject حافظه به آن متصل می شود یا هر یک از اشیاء والد آن از طریق متد Destroy() از بین می رود، آزاد می شود. با این حال، اگر سایر ارجاعات به Scene یا GameObject پاک نشده باشند، حافظه مدیریت شده ممکن است به عنوان یک شی پوسته لو رفته باقی بماند . برای جزئیات بیشتر در مورد اشیاء پوسته مدیریت شده، به کتابچه راهنمای اشیاء پوسته مدیریت شده مراجعه کنید.

علاوه بر این، نشت حافظه می تواند به دلیل اشتراک رویدادها، لامبداها و بسته شدن ها، الحاق رشته ها و مدیریت نامناسب اشیاء جمع شده باشد:

  • برای شروع، به یافتن نشت حافظه مراجعه کنید تا عکس های فوری حافظه Unity را به درستی مقایسه کنید.
  • اشتراک رویداد و نشت حافظه را بررسی کنید. اگر اشیا در رویدادها مشترک شوند (مثلاً توسط نمایندگان یا UnityEvents) اما قبل از نابودی اشتراک خود را به درستی لغو نکنند، مدیر رویداد یا ناشر ممکن است ارجاعاتی به آن اشیاء را حفظ کند. این مانع از جمع آوری زباله آن اشیا می شود که منجر به نشت حافظه می شود.
  • رویدادهای کلاس جهانی یا سینگلتون را که در تخریب شیء ثبت نشده اند، نظارت کنید. به عنوان مثال، لغو اشتراک یا قلاب نمایندگان در تخریب کننده های شی.
  • اطمینان حاصل کنید که تخریب اشیاء ادغام شده به طور کامل ارجاعات به اجزای مش متن ، بافت ها و GameObjects والد را باطل می کند.
  • به خاطر داشته باشید که هنگام مقایسه عکس های Unity Memory Profiler و مشاهده تفاوت در مصرف حافظه بدون دلیل واضح ، ممکن است این تفاوت به دلیل درایور گرافیک یا خود سیستم عامل باشد.

تکه تکه شدن حافظه

تکه تکه شدن حافظه زمانی اتفاق می افتد که بسیاری از تخصیص های کوچک به ترتیب تصادفی آزاد شوند. تخصیص هیپ به صورت متوالی انجام می شود، به این معنی که وقتی فضای حافظه قبلی تمام شود، تکه های حافظه جدید ایجاد می شوند. در نتیجه، اشیاء جدید مناطق خالی تکه های قدیمی را پر نمی کنند و منجر به تکه تکه شدن می شوند. علاوه بر این، تخصیص های موقت بزرگ می تواند باعث تکه تکه شدن دائمی در طول مدت جلسه بازی شود.

این موضوع به ویژه زمانی مشکل ساز است که تخصیص های بزرگ کوتاه مدت در نزدیکی موارد با عمر طولانی انجام شود.

تخصیص گروه بر اساس طول عمر آنها. در حالت ایده آل، تخصیص های طولانی مدت باید با هم و در اوایل چرخه عمر برنامه انجام شود.

ناظران و مدیران رویداد

  • علاوه بر مشکل ذکر شده در بخش (نشت حافظه) 77 ، با گذشت زمان، نشت حافظه می تواند با تخصیص حافظه بلااستفاده به اشیایی که دیگر مورد استفاده قرار نمی گیرند، به تکه تکه شدن کمک کند.
  • اطمینان حاصل کنید که تخریب اشیاء ادغام شده به طور کامل ارجاعات به اجزای مش متن ، بافت ها و GameObjects والد را باطل می کند.
  • مدیران رویداد اغلب فهرست‌ها یا واژه‌نامه‌ها را برای مدیریت اشتراک‌های رویداد ایجاد و ذخیره می‌کنند. اگر اینها در طول زمان اجرا به صورت پویا رشد کرده و کوچک شوند، به دلیل تخصیص و تخصیص مکرر می توانند به تکه تکه شدن حافظه کمک کنند.

کد

  • کوروتین ها گاهی اوقات حافظه را تخصیص می دهند، که به راحتی می توان با ذخیره کردن دستور بازگشت IEnumerator به جای اعلام هر بار یک عبارت جدید، از آن جلوگیری کرد.
  • برای جلوگیری از حفظ ارجاعات ارواح UnityEngine.Object ، وضعیت‌های چرخه حیات اشیاء جمع‌شده را به طور مداوم نظارت کنید.

دارایی ها

  • از سیستم‌های بازگشتی پویا برای تجربه بازی‌های مبتنی بر متن استفاده کنید تا از بارگذاری پیش‌بار همه فونت‌ها برای موارد چند زبانه خودداری کنید.
  • دارایی ها (به عنوان مثال، بافت ها و ذرات) را بر اساس نوع و چرخه عمر مورد انتظار سازماندهی کنید.
  • دارایی‌ها را با ویژگی‌های چرخه عمر بی‌حرکت، مانند تصاویر زائد UI و مش‌های ثابت، متراکم کنید.

تخصیص های مبتنی بر طول عمر

  • برای اطمینان از تخصیص فشرده، دارایی های با عمر طولانی را در شروع چرخه عمر برنامه تخصیص دهید.
  • از NativeCollections یا تخصیص‌دهنده‌های سفارشی برای ساختارهای داده‌ای با حافظه فشرده یا گذرا (به عنوان مثال، خوشه‌های فیزیک) استفاده کنید.

بازی های اجرایی و افزونه ها نیز بر میزان استفاده از حافظه تاثیر می گذارند.

فراداده IL2CPP

IL2CPP متادیتا را برای هر نوع (مثلاً کلاس‌ها، ژنریک‌ها و نمایندگان) در زمان ساخت تولید می‌کند، که سپس در زمان اجرا برای بازتاب، بررسی نوع و سایر عملیات‌های خاص زمان اجرا استفاده می‌شود. این ابرداده در حافظه ذخیره می شود و می تواند به طور قابل توجهی به کل حافظه برنامه کمک کند. حافظه پنهان فراداده IL2CPP سهم قابل توجهی در زمان های اولیه سازی و بارگذاری دارد. علاوه بر این، IL2CPP برخی از عناصر فراداده (مثلاً انواع عمومی یا اطلاعات سریال) را کپی نمی‌کند، که می‌تواند منجر به استفاده زیاد از حافظه شود. این با استفاده از نوع تکراری یا اضافی در پروژه تشدید می شود.

ابرداده IL2CPP را می توان با موارد زیر کاهش داد:

  • اجتناب از استفاده از API های بازتابی ، زیرا آنها می توانند سهم قابل توجهی در تخصیص فراداده IL2CPP داشته باشند.
  • غیرفعال کردن بسته های داخلی
  • اجرای اشتراک گذاری عمومی کامل Unity 2022، که باید به کاهش هزینه های اضافی ناشی از ژنریک کمک کند. با این حال، برای کمک به کاهش بیشتر تخصیص، استفاده از ژنریک ها را کاهش دهید.

حذف کد

علاوه بر کاهش اندازه ساخت، حذف کد همچنین مصرف حافظه را کاهش می دهد. هنگام ساختن در برابر باطن اسکریپت نویسی IL2CPP، حذف بایت کد مدیریت شده (که به طور پیش فرض فعال می شود) کدهای استفاده نشده را از مجموعه های مدیریت شده حذف می کند. این فرآیند با تعریف مجموعه های ریشه و سپس با استفاده از تجزیه و تحلیل کد استاتیک برای تعیین کد مدیریت شده دیگری که آن مجموعه های ریشه استفاده می کنند، کار می کند. هر کدی که در دسترس نباشد حذف می شود. برای اطلاعات بیشتر در مورد حذف کد مدیریت شده، به TTales از ترانشه های بهینه سازی مراجعه کنید: حذف کد مدیریت شده بهتر با پست وبلاگ Unity 2020 LTS و مستندات حذف کد مدیریت شده .

تخصیص دهنده های بومی

برای تنظیم دقیق تخصیص دهنده های حافظه، با تخصیص دهنده های حافظه بومی آزمایش کنید. اگر حافظه بازی کم است، از بلوک های حافظه کوچکتر استفاده کنید، حتی اگر این شامل تخصیص دهنده های کندتر باشد. برای اطلاعات بیشتر به مثال تخصیص دهنده هیپ پویا مراجعه کنید.

پلاگین ها و SDK های بومی را مدیریت کنید

  • افزونه مشکل ساز را پیدا کنید - هر افزونه را حذف کنید و عکس های فوری حافظه بازی را مقایسه کنید. این شامل غیرفعال کردن بسیاری از عملکردهای کد با Scripting Define Symbols و بازسازی کلاس های بسیار جفت شده با رابط ها است. سطح کد خود را با الگوهای برنامه نویسی بازی بررسی کنید تا فرآیند غیرفعال کردن وابستگی های خارجی را بدون غیرقابل بازی کردن بازی شما تسهیل کند.

  • با پلاگین یا نویسنده SDK تماس بگیرید — اکثر افزونه ها منبع باز نیستند.

  • استفاده از حافظه افزونه را بازتولید کنید — می توانید یک افزونه ساده بنویسید (از این افزونه Unity به عنوان مرجع استفاده کنید) که تخصیص حافظه را انجام می دهد. اسنپ شات های حافظه را با استفاده از Android Studio بررسی کنید (زیرا Unity این تخصیص ها را ردیابی نمی کند) یا کلاس MemoryInfo و متد Runtime.totalMemory() را در همان پروژه فراخوانی کنید.

پلاگین Unity جاوا و حافظه بومی را تخصیص می دهد. در اینجا نحوه انجام آن آمده است:

جاوا

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
}