Belleği azaltma

Android bellek yönetimini anladıktan ve oyununuzun bellek kullanımını ölçmek için araçlar ayarladıktan sonraki adım, bellek kullanımını aktif olarak azaltmak ve optimize etmektir. Android'in katı sınırları içinde kalmak, sistemin oyununuzu kapatmasını önlemeye, uzun başlatma sürelerini durdurmaya ve oyununuzun tüm cihazlarda iyi çalışmasını sağlamaya yardımcı olur.

Bu kılavuzda, oyununuzun bellekte kapladığı yeri azaltmaya yönelik pratik teknikler sunulmaktadır. Özellikle öğe düzeyinde optimizasyon, motora özgü yapılandırmalar ve bellek yönetimiyle ilgili en iyi uygulamalara odaklanılmaktadır.

Unity'de belleği azaltma

Unity'nin mimari tasarımı nedeniyle, yerel blok ayırıcılar ve yönetilen yığın genişledikten sonra motor, varlıklar serbest bırakıldıktan sonra bile bu bellek sayfalarını işletim sistemine (OS) hemen döndürmek yerine yeniden kullanmak üzere saklama eğilimindedir. Özellikle, sanal adres alanı (ayrılmış bellek) işlem süresince ayrılmış olarak kalır ve fiziksel bellek (RSS), birkaç çöp toplama (GC) ve kırpma döngüsü gerçekleşene kadar hemen geri alınmaz. Sonuç olarak, geçici bir bellek zirvesi, gerçek kullanım düştükten sonra bile yerleşik belleğin uzun süre şişkin kalmasına neden olabilir. Bu davranış, düşük seviye cihazlarda bellek yetersizliği (OOM) kilitlenmesi riskini artırır ve genel çalışma zamanı kararlılığını düşürür.

Bu nedenle, Unity bellek optimizasyonuna bu motor davranışlarına göre uyarlanmış üç temel sütun üzerinden yaklaşılmalıdır:

  • Öncelikle etkin bellek kullanımını kontrol ederek bellek kullanımında ani artışları önleyin.
  • Gereksiz öğelerin ve yerel kaynakların oluşturulmadığından emin olmak için doku biçimlerini ve gölgelendirici varyantlarını yönetin.
  • GC sıklığını ve yığın genişlemesini en aza indirmek için yönetilen yığında gereksiz ayırmaları ortadan kaldıracak şekilde çalışma zamanı kodu yapılarını yeniden düzenleyin.

Daha fazla bilgi için Unity bellek optimizasyonu başlıklı makaleyi inceleyin.

Unreal Engine'de bellek kullanımını azaltma

Unreal Engine'de, aslına sadık oluşturma işlem hatları ve karmaşık nesne bağımlılığı grafikleri hem anonim RSS'yi hem de dosya destekli bellek baskısını önemli ölçüde artırabilir. Özellikle, sabit referanslara ve derin Plan miras hiyerarşilerine güvenmek, kullanılmayan bağlı öğelerin belleğe yüklenmesine neden olur. Ayrıca, aşırı gölgelendirici permütasyonları, optimize edilmemiş doku akışı havuzları ve sıkıştırılmamış ELF yeniden konumlandırma tabloları, yüksek temel bellek ayak izlerine katkıda bulunarak düşük bellek sonlandırma (LMK) riskini artırır.

Bu nedenle, Unreal Engine bellek optimizasyonuna bu motor davranışlarına göre uyarlanmış üç temel unsur üzerinden yaklaşılmalıdır:

  • Verileri ve mantığı birbirinden ayırın, sabit veya güçlü referansları esnek ya da zayıf referanslarla değiştirin.
  • ASTC sıkıştırması uygularken ve doku akışı havuzlarını cihaz profilleriyle özelleştirirken, ardışık düzen durumu nesnelerini (PSO'lar) ve gereksiz oluşturma hedeflerini en aza indirmek için kullanılmayan mobil aydınlatma özelliklerini ve permütasyon seçeneklerini kaldırın.
  • ELF ikili dosya boyutunu küçültmek ve çalışma zamanı fiziksel bellek ayak izini azaltmak için RELR ve APS Relocation Table Compression'ı etkinleştirin.

Daha fazla bilgi için Unreal bellek optimizasyonu başlıklı makaleyi inceleyin.

Çoklu süreç optimizasyonu

Önbelleğe alınmış işlemlerin bellek kullanımı, etkin uygulamaları etkilemediği için bellek sınırı hesaplamasına dahil edilmez. Hizmeti ayrı bir yalıtılmış süreçte çalıştırmak, ana sürecin mümkün olduğunca hızlı bir şekilde önbelleğe alınmış duruma geçmesine yardımcı olarak oyununuzun durumunu iyileştirir.

Daha fazla bilgi için İşlem durumunu ve belleği izleme, Unity ile hizmet sürecini yalıtma ve Unreal ile hizmet sürecini yalıtma başlıklı makaleleri inceleyin.

Kullanıcı tarafından algılanan hizmetlerde bellek kullanımını azaltma

Oyununuzun, büyük bir indirme işlemini tamamlama veya arka planda sesli sohbet sistemleri gibi kullanım alanları için kullanıcı tarafından algılanan bir hizmette mantık yürütmesi gerekebilir. Bu stratejiler, bu senaryolarda bellek kullanımını yönetmenize ve azaltmanıza yardımcı olabilir.

Büyük boyutlu indirme işlemleriyle ilgili stratejiler

Bu stratejiler, kullanıcı oyununuzu küçülttükten sonra bile devam ettirmek istediğiniz büyük indirme işlemleri için geçerli olabilir.

1. İndirme işlemini yalıtma

Ne: Bellek ayırıcı, bellek havuzu sayfalarını tutabilir ve dizileri boşalttıktan sonra bile Anonim RSS belleğini yapay olarak yüksek tutabilir. Bu nedenle, indirme işlemini ayrı bir süreçte gerçekleştirerek işletim sisteminin uygulamanızın kullanmadığı belleği hemen geri alabilmesini sağlayın. Hizmeti ve işlemi açıkça sonlandırdığınızda veya hizmetten çıktığınızda bellek, işletim sistemi havuzuna geri verilir ve ana işleminiz etkilenmez.

  • Unity'de: İndirme işlemini, özel bir manifest dosyasında android:process=":downloader" gibi bir işlemle tanımlanan yerel bir Android Service'ye yük aktarın ve Unity'nin AndroidJavaClass JNI'si ile çağırın. İndirme işlemi tamamlandığında işlemi sonlandırdığınızdan emin olun. Daha ayrıntılı bilgi için Unity ile ayrı bir süreçte algılanabilir bir hizmet çalıştırma başlıklı makaleye göz atın.

  • Unreal'da: Unreal Plugin Language'i kullanarak android:process=":downloader" gibi bir işlemle özel bir Android Service bildirin ve C++ JNI ile tetikleyin. İndirme işlemi tamamlandığında işlemi sonlandırdığınızdan emin olun. Daha ayrıntılı bilgi için Unreal ile ayrı bir süreçte algılanabilir bir hizmet çalıştırma başlıklı makaleyi inceleyin.

  • Yerel Android için: android:process=":downloader" gibi bir işlemle AndroidManifest içinde Service bildirin. İndirme işlemini bu yalıtılmış süreçte çalıştırın ve indirme işlemi tamamlandığında Process.killProcess(Process.myPid()) işlevini çağırın.

Bu özelliğin faydaları: Belleğin tutulma süresini kısaltır ve daha büyük ana işlem tarafından kullanılan belleği serbest bırakırken indirme işleminin devam etmesine olanak tanır.

2. İndirilenleri doğrudan diske aktarma

Ne: Ağ yanıtlarını yazmadan önce büyük bir dizide biriktirmek yerine küçük, sabit boyutlu ve yeniden kullanılabilir bir arabellek kullanarak verileri doğrudan ağ soketinden diske aktarın.

  • Unity'de: Dosya boyutuna eşdeğer bir yerel bellek arabelleği (anonim RSS belleği) ayırdığı için DownloadHandlerBuffer öğesini öğe paketleri veya büyük dosyalar için kullanmaktan kaçının. Bunun yerine, DownloadHandlerFile kullanarak baytları arka plan iş parçacığında yerel olarak diske aktarın.

  • Unreal'da: Gelen veri parçalarını doğrudan FArchive (Unreal'ın Dosya Yöneticisi'ni kullanan, dosya destekli bir arşiv) içine aktarın. TArray<uint8> içine IHttpRequest öğesinden yük eklemek yerine SetResponseBodyReceiveStream() kullanın.

  • Yerel Android için: HTTP yanıtında .readBytes() veya .string() çağrısı yapmak yerine, InputStream öğesini bir arabellek havuzu kullanarak FileOutputStream öğesine yönlendirin.

Nasıl yardımcı olur? En yüksek bellek kullanımını azaltır.

3. İndirilen dosyaların akışla aktarılırken sıkıştırmasının açılması

Ne: İndirmeniz sıkıştırılmışsa dosyayı indirip RAM'e yükleyip ardından ayıklamak yerine ağ giriş akışınızı ZipInputStream gibi bir akışlı sıkıştırma açıcıya sarın.

Nasıl yardımcı olur? En yüksek bellek kullanımını azaltır.

4. İşletim sistemine yetki verme

Ne: Arka plan belleğini yönetmekten tamamen kaçınmak için işi Android'in yerel API'lerine devredin.

  • WorkManager, işletim sistemi düzeyindeki JobScheduler için modern ve önerilen sarmalayıcıdır. Android 14 ve sonraki sürümlerde WorkManager, kullanıcı tarafından tetiklenen indirmeleri otomatik olarak kullanıcı tarafından başlatılan veri aktarımı (UIDT) işi olarak işler. Bu, uygulamanızın sürecinde çalışır. Bu nedenle, bellek kullanımını en aza indirmek için doğrudan diske aktarmaya devam etmeniz gerekir. UIDT, sistem kaynakları sınırlı hale gelirse işletim sisteminin indirme işleminizi sorunsuz bir şekilde duraklatıp devam ettirmesine izin vererek uygulamanızı düşük bellek kilitlenmelerine karşı korur.

  • DownloadManager ayrı bir sistem sürecinde çalışır ve indirme için bellek kullanımını uygulamanıza atfetmez. Dosya indirildiğinde ve hazır olduğunda uygulamanız bir yayın bildirimi alır.

Bu nasıl yardımcı olur? WorkManager, düşük bellek senaryolarıyla başa çıkmanıza yardımcı olur ve DownloadManager, uygulamanızın bellek kullanımını azaltır.

Yardımcı hizmetlerle ilgili stratejiler

Bu stratejiler, bir oyunun ana oyun süreciyle paralel olarak çalıştırdığı yardımcı hizmetler (ör. arka planda sesli sohbet) için geçerli olabilir.

1. İşlemi yalıtma

Ne: Özelliği (ör. sesli sohbet çözümünüz) ana oyun motorundan ayırın. Örneğin, mikrofon yakalama ve ağ akışı işlemlerini ayrı bir işleme atanmış bir Android ön plan hizmetinde (Manifest'te android:process=":voice" ile olduğu gibi beyan edilir) çalıştırabilirsiniz.

Bu özellik nasıl yardımcı olur? Uygulama simge durumuna küçültüldüğünde, ağır ana oyun süreci daha düşük öncelikli bir önbelleğe alınmış duruma geçebilir. Bu sırada daha hafif yardımcı hizmet, kullanıcı tarafından algılanan hizmet durumunda devam eder.

2. Kullanılmayan işlem içi belleği kırpma

Ne: Yardımcı hizmet, oyun motoruna ayrılmayacak kadar derinlemesine entegre edilmişse oyun arka plana alındığında/küçültüldüğünde işlemden mümkün olduğunca fazla yükü kaldırmaya çalışın. Doku önbelleklerini boşaltmayı, gereksiz olmayan sahneleri kaldırmayı, motorun tik ve oluşturma hızlarını 0'a düşürmeyi ve atık toplama işlemini açıkça çağırmayı deneyin.

Bu nasıl yardımcı olur? Oyun ön planda değilken gerekmeyen bellek kullanımını azaltır.