{"id":2181,"date":"2020-08-20T16:22:50","date_gmt":"2020-08-20T08:22:50","guid":{"rendered":"http:\/\/blog.coolcoding.cn\/?p=2181"},"modified":"2020-08-20T16:28:03","modified_gmt":"2020-08-20T08:28:03","slug":"memory-management-in-unity","status":"publish","type":"post","link":"https:\/\/blog.coolcoding.cn\/?p=2181","title":{"rendered":"[Unity][\u6559\u7a0b]Memory Management in Unity"},"content":{"rendered":"\n<p>Proper memory management in Unity can be challenging. The goal of this guide is to fit you with the necessary knowledge to profile and optimize memory consumption on any publicly available platform. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1.A guide to optimizing Memory<\/h2>\n\n\n\n<p>Proper memory management in Unity can be challenging. You need to consider the nuances of automatic memory management, understand the difference between native and managed memory, and how memory behaves on different platforms. The goal of this guide is to fit you with the necessary knowledge to profile and optimize memory consumption on any publicly available platform. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2.Managed Memory<\/h2>\n\n\n\n<p>Understanding the managed heap is essential for memory management in Unity. For more information on profiling managed memory and how to optimize memory, see the <a href=\"https:\/\/docs.unity3d.com\/Manual\/BestPracticeUnderstandingPerformanceInUnity4-1.html\" target=\"_blank\" rel=\"noreferrer noopener\">understanding managed memory section<\/a> of the <a href=\"https:\/\/docs.unity3d.com\/Manual\/BestPracticeUnderstandingPerformanceInUnity2.html\" target=\"_blank\" rel=\"noreferrer noopener\">Understanding Optimization in Unity guide<\/a>.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Assets<\/h1>\n\n\n\n<p>Assets cause <a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/best-practices\/native-memory#Assets\" target=\"_blank\" rel=\"noreferrer noopener\">native memory<\/a> and managed memory implication during runtime. The Unity runtime doesn&#8217;t return managed memory to the operating system for reuse until the user terminates the application. The managed heap grows if it becomes too fragmented and runs out of available memory. Due to this unpredictable behavior, it is critical to know how Assets occupy managed memory:<\/p>\n\n\n\n<ul><li>Use <strong>Destroy(myObject)<\/strong> to destroy an <a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Object.Destroy.html\" target=\"_blank\" rel=\"noreferrer noopener\">Object<\/a> and release its memory. Setting a reference to a Object to null does <strong>not<\/strong> destroy it.<\/li><li>You should set persistent (long-term) objects as classes and ephemeral (short-term) objects as structs. Structs are not allocated on the heap and thus not garbage collected.<\/li><li>Reuse <a href=\"https:\/\/docs.unity3d.com\/Manual\/BestPracticeUnderstandingPerformanceInUnity4-1.html\" target=\"_blank\" rel=\"noreferrer noopener\">temporary work buffers<\/a> to keep temporary garbage low, instead of allocating frequently.<\/li><li>An <a href=\"https:\/\/docs.unity3d.com\/Manual\/BestPracticeUnderstandingPerformanceInUnity4-1.html\" target=\"_blank\" rel=\"noreferrer noopener\">Enumerator<\/a> does not clean up its memory until it exits.<\/li><li>You should avoid <a href=\"https:\/\/docs.unity3d.com\/Manual\/BestPracticeUnderstandingPerformanceInUnity3.html\" target=\"_blank\" rel=\"noreferrer noopener\">never-ending Coroutines<\/a>, especially when allocating large amounts of managed memory within on, as Coroutines hold onto stack allocations on the heap until the Coroutine ends.<\/li><\/ul>\n\n\n\n<p>Please read the <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.unity3d.com\/Manual\/BestPracticeUnderstandingPerformanceInUnity4-1.html\" target=\"_blank\">understanding managed memory section<\/a> of the <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.unity3d.com\/Manual\/BestPracticeUnderstandingPerformanceInUnity2.html\" target=\"_blank\">Understanding Optimization in Unity guide<\/a> to gain a deeper understanding of the managed heap.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3.IL2CPP &amp; Mono<\/h2>\n\n\n\n<p>On <strong>iOS<\/strong> and <strong>Android,<\/strong> choose between the Mono or IL2CPP Scripting Backends in Player Settings. To change the Scripting Backend, go to the <strong>Player Settings<\/strong> window (menu: <strong>Edit &gt; Project Settings &gt; Player<\/strong>), scroll down to the <strong>Other Settings<\/strong> section, and select either <strong>Mono<\/strong> or <strong>IL2CPP<\/strong> from the <strong>Scripting Backend<\/strong> drop-down menu.<strong>Note<\/strong>: As of 2017.3, choose either the <a href=\"https:\/\/docs.unity3d.com\/Manual\/IL2CPP.html\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>IL2CPP<\/strong><\/a> Scripting Backend or the <strong>Mono Scripting<\/strong> Backend. However, both WebGL and UWP only support IL2CPP. iOS still supports the Mono Scripting Backend for fast iteration, but you cannot submit Mono (32-bit) application to Apple anymore.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Benefits and drawbacks of different Scripting Backends<\/h1>\n\n\n\n<p>Each Scripting Backend has benefits and drawbacks that should influence your decision on which is the right choice for your situation:IL2CPP<\/p>\n\n\n\n<ul><li>Code generation is heavily improved compared to Mono.<\/li><li>Debugging Script code in C++ from top to bottom is possible.<\/li><li>You can enable <a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/best-practices\/il2cpp-mono#Code%20stripping%20in%20Unity\" target=\"_blank\" rel=\"noreferrer noopener\">Engine code stripping<\/a> to reduce code size.<\/li><li>Build times are longer than with Mono.<\/li><li>Only supports <a href=\"https:\/\/docs.unity3d.com\/Manual\/ScriptingRestrictions.html\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Ahead of Time<\/strong><\/a> <a href=\"https:\/\/docs.unity3d.com\/Manual\/ScriptingRestrictions.html\" target=\"_blank\" rel=\"noreferrer noopener\">(AOT)<\/a> compilation.<\/li><\/ul>\n\n\n\n<p>Mono<\/p>\n\n\n\n<ul><li>Faster build times than IL2CPP.<\/li><li>Supports more managed libraries due to <strong>Just In Time compilation<\/strong> (JIT).<\/li><li>Supports runtime code execution.<\/li><li>Must ship managed assemblies (<em>.dll<\/em> files that mono- or <em>.net<\/em>&#8211; produces).<\/li><\/ul>\n\n\n\n<p><strong>Tip:<\/strong> You should use IL2CPP to both develop and ship your Project. If iteration times end up being too slow using IL2CPP, switch temporarily to the Mono Scripting Backend during development to improve iteration speed.<strong>Note<\/strong>: The default target architectures in the Player Settings are optimized for <strong>release builds<\/strong>. Using this default during development increases your build time because Unity builds the binary for each target architecture selected:<\/p>\n\n\n\n<ul><li>The default <strong>Target Architecture<\/strong> in the Android Player Settings are armv7 <strong>and<\/strong> x86 with the IL2CPP <strong>and<\/strong> Mono Scripting Backend.<\/li><li>The default <strong>Architecture<\/strong> in the iOS Player Settings are armv7 <strong>and<\/strong> arm64 with the IL2CPP Scripting Backend.<\/li><\/ul>\n\n\n\n<h1 class=\"wp-block-heading\">Code stripping in Unity<\/h1>\n\n\n\n<p>Code size has a direct influence on disk space and runtime memory. It\u2019s important that Unity removes any code paths you aren\u2019t using from the code base. Unity strips code automatically during a build, working on two different levels:<\/p>\n\n\n\n<ul><li>Managed code stripping<\/li><li>Native code stripping<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Managed code stripping<\/h2>\n\n\n\n<p>Unity strips managed code at the <a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/StrippingLevel.StripAssemblies.html\" target=\"_blank\" rel=\"noreferrer noopener\">method level<\/a>. To change the Stripping Level, go to the <strong>Player Settings<\/strong> window, scroll down to the <strong>Other Settings<\/strong> section, locate the <strong>Stripping Level<\/strong> drop-down menu and select <strong>Strip Assemblies<\/strong> .The UnityLinker removes unused types (classes, structs, etc.) from the <a href=\"https:\/\/en.wikipedia.org\/wiki\/Common_Intermediate_Language\" target=\"_blank\" rel=\"noreferrer noopener\">Intermediate Language<\/a> (IL). Even if you use a type, the UnityLinker removes the type\u2019s unused methods.<strong>Note<\/strong>: Although this functionality is optional on builds using the Mono Scripting Backend, it is always enabled on builds using the IL2CPP Scripting Backend.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Native code stripping<\/h2>\n\n\n\n<p>Unity enables <strong>Strip Engine Code<\/strong> by default in the <strong>Player Settings<\/strong> and enables <a href=\"https:\/\/docs.unity3d.com\/Manual\/iphone-playerSizeOptimization.html\" target=\"_blank\" rel=\"noreferrer noopener\">native code stripping<\/a>. Enable <strong>Strip Engine Code<\/strong> to remove unused modules and classes in the native Unity engine code. Disable <strong>Strip Engine Code<\/strong> to preserve all of the modules and classes in the native Unity engine code.<strong>Note:<\/strong> For publically available platforms, native code stripping is only supported on iOS, WebGL, and Android.Unity 2017.3 onwards supports native code stripping on Android; in prior versions, the Unity Android runtime shipped as a pre-linked <em>.so<\/em> library, which Unity could not strip. The Android runtime shipped in 2017.3 is a static engine code library, allowing for native code stripping. The final link happens during the build, which is ultimately what accounts for the slightly longer build times.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Unity Module Stripping<\/h1>\n\n\n\n<p><strong>Note<\/strong>: WebGL is currently the only platform which supports stripping unused Unity modules.Unity makes the best attempt to eliminate all unused Unity modules. This means if any Scene uses or any script references a component from a Unity module you include in the build, Unity doesn&#8217;t strip the module. Unity doesn\u2019t strip core modules, such as Camera, AssetBundle, Halo, etc. but in future releases, Unity strips these too.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Stripping modules from an empty project on WebGL<\/h2>\n\n\n\n<p>Removing modules saves a substantial amount of memory. For example, one of the largest modules in Unity is the physics module, which accounts for about 5MB of gzipped ASM.js code. If you remove the physics module of an empty Project it reduces the build size from 17MB to 12MB.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">C# Code Stripping<\/h1>\n\n\n\n<p>The UnityLinker works on a basic mark and sweep principle, similar to a garbage collector. The UnityLinker builds a map of each type and method included in each assembly from a build. The UnityLinker marks a number of types and methods as <em>&#8220;roots&#8221;<\/em> and the UnityLinker then walks the graph of dependencies between types and methods.If, for example, one type\u2019s method calls a method on another type, then the Unity Linker marks the called type and methodas <em>in-use<\/em>. Once the UnityLinker marks all the roots\u2019 dependencies, the system reweaves the assemblies, omitting methods or entire types that are not marked as used.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Roots from Scenes, Resources, Assemblies, and AssetBundles<\/h2>\n\n\n\n<p>The UnityLinker marks its internal classes as roots if they\u2019ve been used in a Scene or from content in Resources. Similarly, the UnityLinker marks all types and methods in the user assemblies as roots.If you use types and methods from other assemblies directly in a Scene or in an Asset you include in resources, Unity marks these as roots.Use the <a href=\"https:\/\/docs.unity3d.com\/Manual\/IL2CPP-BytecodeStripping.html\" target=\"_blank\" rel=\"noreferrer noopener\">link.xml<\/a> file to mark additional types and methods as roots. If your Project uses AssetBundles, use the <a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/BuildPlayerOptions-assetBundleManifestPath.html\" target=\"_blank\" rel=\"noreferrer noopener\">BuildPlayerOption.assetBundleManifestPath<\/a> to mark additional types and methods as roots too.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">User Assemblies<\/h2>\n\n\n\n<p>User Assemblies are the assemblies Unity generates from loose code within the Assets folder. Unity places most of the C# code in <strong>Assembly-CSharp.dll<\/strong>; whereas Unity places code in <strong>\/Assets\/Standard Assets\/<\/strong> or <strong>\/Assets\/Plugins\/<\/strong> in <strong>Assembly-CSharp-firstpass.dll<\/strong>, which is also considered a user assembly.If a significant proportion of a codebase\u2019 types or methods are unused, you could save some binary-size and build time by migrating stable code into pre-built assemblies and allowing the UnityLinker to strip them. Use <a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/best-practices\/il2cpp-mono#Assembly%20Definition%20Files\" target=\"_blank\" rel=\"noreferrer noopener\">Assembly Definition Files<\/a> to migrate stable code into pre-built assemblies.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Generic Sharing<\/h1>\n\n\n\n<p>For reference types, IL2CPP generates the implementation (C++ code) which can be shared between Generics using reference types. However, IL2CPP doesn&#8217;t share value types because IL2CPP needs to generate the code for each of the types separately. This results in your code size increasing.In general, there should not be any noticeable performance difference, but it depends on the specific use-case and what it should be optimized for. Classes are usually on the heap while structs are on the stack (with some exceptions, such as in the case of coroutines). For memory performance and usage this matters, and using non-reference types leads to other problems. You must copy function parameters using value types to influence performance. For additional information see this <a href=\"https:\/\/blogs.unity3d.com\/2015\/06\/16\/il2cpp-internals-generic-sharing-implementation\/\" target=\"_blank\" rel=\"noreferrer noopener\">blog post<\/a> for more information. Be warned though that Integer or Enum types are <em>not<\/em> shared at this moment.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Assembly Definition Files<\/h1>\n\n\n\n<p><a href=\"https:\/\/blogs.unity3d.com\/2017\/11\/22\/unity-2017-3b-feature-preview-assembly-definition-files-and-transform-tool\/\" target=\"_blank\" rel=\"noreferrer noopener\">Assembly Definition Files<\/a> allow you to define custom managed assemblies and assign user Scripts to them on a per-folder basis.In turn, this results in faster iteration times, because Unity will only build those assemblies actually affected by script changes.<strong>Note<\/strong>: While multiple assemblies do grant modularity, they also increase the application\u2019s binary size and runtime memory. Tests show that the executable can grow by up to 4kB per assembly.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Build Report<\/h1>\n\n\n\n<p><a href=\"https:\/\/docs.unity3d.com\/2018.1\/Documentation\/ScriptReference\/Build.Reporting.BuildReport.html\" target=\"_blank\" rel=\"noreferrer noopener\">Build Report<\/a> is an API which is included in Unity but has no UI yet. Building a Project generates a <em>buildreport<\/em> file which lets you discover what is stripped and why it was stripped from the final executable.To preview the stripping information:<\/p>\n\n\n\n<ol><li>Build your Project.<\/li><li>Leave the Editor open.<\/li><li>Connect to <a href=\"http:\/\/files.unity3d.com\/build-report\/\" target=\"_blank\" rel=\"noreferrer noopener\">http:\/\/files.unity3d.com\/build-report\/<\/a>.<\/li><\/ol>\n\n\n\n<p>The Build Report tool connects to your running Unity Editor, downloads and presents the breakdown of the build report.It\u2019s possible to use the <strong>binary2text<\/strong> tool on the generated file in <strong>Library\/LatestBuild.buildreport<\/strong> to view data from the report. <a rel=\"noreferrer noopener\" href=\"https:\/\/support.unity3d.com\/hc\/en-us\/articles\/217123266-How-do-I-determine-what-is-in-my-Scene-bundle-\" target=\"_blank\">Binary2text<\/a> is shipped with Unity under <strong>Unity.app\/Contents\/Tools\/<\/strong> on Mac or <strong>Unity\/Editor\/Data\/Tools\/<\/strong> on Windows. The build report is available in Unity 5.5 and later.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4.Native Memory<\/h2>\n\n\n\n<p>Native memory is a key component when optimizing applications as most of the engine code is in resident memory. When you integrate code in Native Plugins you can control it directly, however it is not always possible to control and optimize the native memory consumption from Unity internal systems. Internal systems use different buffers and resources, and it may not always be apparent how that influences memory consumption. The following section details Unity internal systems and explains memory data you often see in a native profiler.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Native Buffer<\/h1>\n\n\n\n<p>Unity uses many different native allocators and buffers. Some are persistent, such as the constant buffer, while others are dynamic, such as the back buffer. The following subsections describe buffers and their behavior.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Scratchpad <\/h1>\n\n\n\n<p>Unity stores constants in a 4MB buffer pool and cycles through the pool between frames. The pool is bound to the GPU for the duration of its lifetime and shows up in frame capture tools such as XCode or Snapdragon.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/connect-prd-cdn.unity.com\/20190130\/9dc3f46a-8502-4786-aeb1-5a2266d0bcae_memory_management_in_unity_0.png\" alt=\"\"\/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Block allocator<\/h1>\n\n\n\n<p>Unity uses block allocators in some internal systems. There is memory and CPU overhead anytime Unity needs to allocate a new page block of memory. Usually, the block size of the page is large enough that the allocation only appears the first time Unity uses a system. After the first allocation, the page block is reused. There are small differences in how internal systems use the block allocator.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">AssetBundles<\/h1>\n\n\n\n<p>The first time you load an AssetBundle, additional CPU and memory overhead is required as the block allocators spin up, allowing the Asset Bundle system to allocate the first page block of memory.Unity reuses the pages that the Asset Bundle system allocates, however, if you want to load many Asset Bundles at once you may have to allocate a second or third block. All of these stay allocated until the application terminates.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Resources<\/h1>\n\n\n\n<p>Resources use a block allocator shared with other systems, so there is no CPU or memory overhead when loading an Asset from Resources for the first time (as it already happened earlier during startup).<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Ringbuffer<\/h1>\n\n\n\n<p>Unity uses a ring buffer to push textures to the GPU. You can adjust this async texture buffer via <a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/QualitySettings-asyncUploadBufferSize.html\" target=\"_blank\" rel=\"noreferrer noopener\">QualitySettings.asyncUploadBufferSize<\/a>. <strong>Note<\/strong>: You cannot return Ring buffer memory to the system after Unity allocates it.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Assets<\/h1>\n\n\n\n<p>Assets cause native and <a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/best-practices\/managed-memory#Assets\" target=\"_blank\" rel=\"noreferrer noopener\">managed memory<\/a> implications during runtime. Beyond managed memory, Unity returns native memory to the operating system when no longer needed. Since every byte counts &#8211; especially on mobile devices &#8211; you can try the following to reduce native runtime memory:<\/p>\n\n\n\n<ul><li>Remove unused channels from meshes<\/li><li>Remove redundant keyframes from animations<\/li><li>Use <a href=\"https:\/\/docs.unity3d.com\/Manual\/class-QualitySettings.html\" target=\"_blank\" rel=\"noreferrer noopener\">maxLOD<\/a> in the <strong>Quality Settings<\/strong> to remove higher detail meshes in <a href=\"https:\/\/docs.unity3d.com\/Manual\/LevelOfDetail.html\" target=\"_blank\" rel=\"noreferrer noopener\">LODGroups<\/a> from the build<\/li><li>Check the <a href=\"https:\/\/docs.unity3d.com\/Manual\/LogFiles.html\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Editor.log<\/strong><\/a> after a build to ensure that the size of each Asset on disk is proportional to its runtime memory use<\/li><li>Reduce memory uploaded to GPU memory by using the <strong>Texture Quality<\/strong> setting in the <strong>Rendering<\/strong> section of the <strong>Quality Settings<\/strong> to force lower texture resolutions via mipmaps<\/li><li>Normal maps need not be the same size as diffuse maps (1:1), so you can use a smaller resolution for normal maps while still achieving high visual fidelity and saving memory and disk space<\/li><\/ul>\n\n\n\n<p>Be aware that <a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/best-practices\/managed-memory#Assets\" target=\"_blank\" rel=\"noreferrer noopener\">managed memory<\/a> implications can often surpass native memory problems, due to heavy fragmentation of the managed heap.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Cloned Materials<\/h1>\n\n\n\n<p>Beware of cloned materials, as accessing the material property of any renderer causes the material to be cloned even if nothing is assigned. This cloned material will not be garbage collected and is only cleared up when you change Scenes or call <a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Resources.UnloadUnusedAssets.html\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Resources.UnloadUnusedAssets()<\/em><\/a>. You can use customRenderer.sharedMaterial if you want to access a read-only material.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Unloading Scenes<\/h1>\n\n\n\n<p>Call <a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/SceneManagement.SceneManager.UnloadSceneAsync.html\" target=\"_blank\" rel=\"noreferrer noopener\">UnloadScene()<\/a> to destroy and unload the GameObjects associated with a Scene. <strong>Note<\/strong>: This does not unload the associated Assets. In order to unload the Assets and free both managed and native memory, you need to call <a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Resources.UnloadUnusedAssets.html\" target=\"_blank\" rel=\"noreferrer noopener\">Resources.UnloadUnusedAssets()<\/a> after the Scene has been unloaded.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Audio<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Virtual Voices<\/h2>\n\n\n\n<p>Unity dynamically sets voices as either virtual or real, depending on the <a href=\"https:\/\/www.fmod.com\/docs\/api\/content\/generated\/overview\/virtualvoices.html\" target=\"_blank\" rel=\"noreferrer noopener\">real time audibility<\/a> of the platform. For example, Unity sets sounds that are playing far off or with a low volume as virtual, but will change these sounds to a real voice if they come closer or become louder. The default values in the <a href=\"https:\/\/docs.unity3d.com\/Manual\/class-AudioManager.html\" target=\"_blank\" rel=\"noreferrer noopener\">Audio Settings<\/a> are great values for mobile devices.<\/p>\n\n\n\n<table class=\"wp-block-table\"><tbody><tr><td><\/td><td> <strong>Max Virtual Voice Count<\/strong> <\/td><td> <strong>Max Real Voice Count<\/strong> <\/td><\/tr><tr><td>Default<\/td><td>512<\/td><td>32<\/td><\/tr><tr><td>Maximum<\/td><td>4095<\/td><td>255<\/td><\/tr><\/tbody><\/table>\n\n\n\n<h2 class=\"wp-block-heading\">DSP Buffer Size<\/h2>\n\n\n\n<p>Unity uses the DSP buffer sizes to control the <a href=\"https:\/\/www.fmod.com\/docs\/api\/content\/generated\/FMOD_System_SetDSPBufferSize.html\" target=\"_blank\" rel=\"noreferrer noopener\">mixer latency<\/a>. The underlying Audio System <a href=\"https:\/\/www.fmod.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">FMOD<\/a> defines the platform dependent DSP buffer sizes. The buffer size influences the latency and should be treated carefully. The <em>number of buffers<\/em><a href=\"https:\/\/www.fmod.com\/docs\/api\/content\/generated\/FMOD_System_SetDSPBufferSize.html\" target=\"_blank\" rel=\"noreferrer noopener\">defaults<\/a> to 4. The audio system in Unity uses the following sample counts for the Audio Settings in Unity:<\/p>\n\n\n\n<table class=\"wp-block-table\"><tbody><tr><td><strong>Latency = Samples * Number of Buffers<\/strong><\/td><td><strong>Samples<\/strong><\/td><td><strong>Number of Buffers<\/strong><\/td><\/tr><tr><td>Default<\/td><td>iOS &amp; Desktop: 1024 Android: 512<\/td><td>4<\/td><\/tr><tr><td>Best latency <\/td><td>256<\/td><td>4<\/td><\/tr><tr><td>Good latency<\/td><td>512<\/td><td>4<\/td><\/tr><tr><td>Best performance<\/td><td>1024<\/td><td>4<\/td><\/tr><\/tbody><\/table>\n\n\n\n<h1 class=\"wp-block-heading\">Audio Import Settings<\/h1>\n\n\n\n<p>Using the correct settings can save runtime memory and CPU performance.<\/p>\n\n\n\n<ul><li>Enable <strong><em>Force to mono<\/em><\/strong> option on audio files if they do not require stereo sound; doing so will reduce runtime memory and disk space. This is mostly used on <em>mobile platforms<\/em> with a mono speaker.<\/li><li>Larger AudioClips should be set to <strong>Streaming<\/strong>. Streaming in Unity 5.0 and later has a <a href=\"https:\/\/docs.unity3d.com\/Manual\/class-AudioClip.html\" target=\"_blank\" rel=\"noreferrer noopener\">200KB overhead<\/a> so you should set audio files smaller than 200KB to <strong><em>Compressed into Memory<\/em><\/strong> instead.<\/li><li>For longer clips, import AudioClips as <strong>Compressed into Memory<\/strong> to save runtime memory (if the clips are not set to Streaming).<\/li><li>Use <strong><em>Decompress On Load<\/em><\/strong> only if you have plenty of memory but are constrained by CPU performance, as this option requires<a href=\"https:\/\/docs.unity3d.com\/Manual\/class-AudioClip.html\" target=\"_blank\" rel=\"noreferrer noopener\"> a significant amount of memory<\/a>.<\/li><\/ul>\n\n\n\n<p>Various platforms also have preferred <strong>Compression Format<\/strong> settings to save runtime memory and disk space:<\/p>\n\n\n\n<ol><li>Set <strong>Compression Format<\/strong> to <strong>ADPCM<\/strong> for very short clips such as sound effects which are played often. ADPCM offers a fixed 3.5:1 compression ratio and is inexpensive to decompress.<\/li><li>Use <strong>Vorbis<\/strong> on <strong>Android<\/strong> for longer clips. Unity does not use hardware accelerated decoding.<\/li><li>Use <strong>MP3<\/strong> or Vorbis on <strong>iOS<\/strong> for longer clips. Unity does not use hardware accelerated decoding.<\/li><li>MP3 or Vorbis need more resources for decompression but offer significantly smaller file size. High-quality MP3s require fewer resources for decompression, while middle and low-quality files of either format require almost the same CPU time for decompression.<\/li><li><strong>Tip<\/strong>: Use <strong>Vorbis<\/strong> for longer looping sounds since it handles looping better. MP3 contains data blocks of predetermined sizes so if the loop is not an exact multiple of the block size then the MP3 encoding will add silence while Vorbis does not.<\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">5.Android Memory Management<\/h2>\n\n\n\n<p>Memory on Android is shared across multiple processes. How much memory a process uses is not clear at first glance. Android memory management is complex but there is a great talk on <a href=\"https:\/\/www.youtube.com\/watch?v=w7K0jio8afM\" target=\"_blank\" rel=\"noreferrer noopener\"><em>Understanding Android memory usage from Google I\/O 2018<\/em><\/a> which you should take a look at before reading on.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Paging on Android<\/h1>\n\n\n\n<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Paging\" target=\"_blank\" rel=\"noreferrer noopener\">Paging<\/a> is a method of moving memory from main memory to secondary memory or vice versa.Android pages out to disk but does not use swap space for paging the memory. This makes it even more difficult to see the total memory, especially as every application in Android runs in a different process which runs its own instance of a Dalvik VM.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Paging vs swap space<\/h2>\n\n\n\n<p>Android uses paging but does not utilize swap space. Paging relies heavily on the ability to memory map (<em>mmap()<\/em>) files and store the kernel page in data as needed. Although this doesn\u2019t happen often, paging needs to drop kernel pages when memory is low and the system drops cache page files. Android does not swap spaces for paging out dirty pages, as doing so on mobile devices both lowers battery life and causes excess wear-and-tear on memory.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Onboard flash<\/h2>\n\n\n\n<p>Android devices frequently come with very little onboard flash and limited space to store data. This is mainly used to store apps but could actually store a swap file. Onboard flash is slow and has generally worse access rates than those of hard disks or flash drives. Although onboard flash size has improved recently, it is still not enough to enable swapping spaces effectively. A basic rule of thumb for swap file size is about 512MB per 1-2GB RAM. You can always enable swap support by modifying the kernel .config file (CONFIG_SWAP) and compiling the kernel yourself, but doing so falls outside the scope of this guide.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Memory Consumption Limitations<\/h1>\n\n\n\n<p>Just how much memory can your app use before the <a href=\"https:\/\/developer.android.com\/guide\/components\/activities\/process-lifecycle\" target=\"_blank\" rel=\"noreferrer noopener\">Android system<\/a> (aka memory killer) activates and starts shutting down processes? Unfortunately, there is no simple answer, and figuring it out involves a lot of profiling with the tools such as dumpsys, procrank, and Android Studio.Many different factors can influence your ability to measure memory consumption on Android such as the following:<\/p>\n\n\n\n<ul><li>Different platform configuration for low, mid, and high-end devices<\/li><li>Different OS versions on the test device(s)<\/li><li>Different points in your application when you measure memory<\/li><li>Overall device memory pressure<\/li><\/ul>\n\n\n\n<p>It is important to always measure your memory at the same location in your code with the same platform configuration, OS version, and device memory pressure.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Low and high memory pressure<\/h2>\n\n\n\n<p>A good way to profile memory is to ensure that the device has plenty of free memory available (low memory pressure) while you profile the memory consumption of your application. If the device has no free memory available (high memory pressure) it can be difficult to get stable results. It\u2019s important to keep in mind that although you use profiling to try to find the source of high memory pressure, there are still hard physical limitations. If the system is already thrashing memory caches, it will produce unstable results during memory profiling your app.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Dumpsys<\/h1>\n\n\n\n<p>If you were to sum up all physical RAM mapped to each process, then add up all of the processes, the resulting figure would be greater than the actual total RAM. With <em>dumpsys,<\/em> you can get clearer information about each Java process. The stats dumpsys provides contain a variety of information related to the apps\u2019 memory. <em>dumpsys<\/em> is an Android tool that runs on the device and dumps information about the status of system services and applications. dumpsys enables you to easily access system information.<\/p>\n\n\n\n<ul><li>Get system information in a simple string representation.<\/li><li>Use dumped CPU, RAM, battery, and storage to check how an application affects the overall device.<\/li><\/ul>\n\n\n\n<p>The following command lists all services offered by dumpsys:<em>~$ adb shell dumpsys | grep &#8220;dumpsys services&#8221;<\/em>You can use <em>dumpsys meminfo<\/em> to dump system memory on Android.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">dumpsys meminfo<\/h1>\n\n\n\n<p>adb provides a host of tools to gain information about the memory of a running application on Android. The most common and quickest way to get an overview is the <strong><em>adb shell dumpsys meminfo<\/em><\/strong> command. It reports detailed information about the memory usage of each Java process, native heap, binary data as well as a variety of process and system information. The following command will provide a quick overview of system memory:<em>~$ adb shell dumpsys meminfo<\/em>It\u2019s possible to track a single process via <strong>name<\/strong>, <strong>bundle ID<\/strong> or <strong>pid<\/strong> to determine the details of the Unity <strong><em>androidtest<\/em><\/strong> app as the following command shows. The androidtest app is an empty Unity Project with only one main Scene, no Skybox, and no content, to get a baseline for memory measurements.<em>~$ adb shell dumpsys meminfo com.unity.amemorytest<\/em>This prints the following information in the command line using a Nexus 6P (2560 by 1440 px &#8211; Android 8.1.0 and Unity 2018.1). <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>* Applications Memory Usage (in Kilobytes):  \n* Uptime: 6815563691 Realtime: 10882940478  \n*   \n* ** MEMINFO in pid 20676 [com.unity.androidtest] **  \n*                    Pss  Private  Private  SwapPss     Heap     Heap     Heap  \n*                  Total    Dirty    Clean    Dirty     Size    Alloc     Free  \n*                 ------   ------   ------   ------   ------   ------   ------  \n*   Native Heap    31467    31448        0        0    51072    47261     3810  \n*   Dalvik Heap     1872     1760        0        0    12168     7301     4867  \n*  Dalvik Other      470      460        0        0                             \n*         Stack      492      492        0        2                             \n*        Ashmem        8        0        0        0                             \n*       Gfx dev     3846     2036        0        0                             \n*     Other dev        4        0        4        0                             \n*      .so mmap    17760      516    15908      161                             \n*     .jar mmap        4        0        4        0                             \n*     .apk mmap      243        0        0        0                             \n*     .dex mmap      116        4      112        0                             \n*     .oat mmap     6206        0     3244        0                             \n*     .art mmap     2571      716      232       22                             \n*    Other mmap       49        4        0        2                             \n*    EGL mtrack    99840    99840        0        0                             \n*     GL mtrack    64480    64480        0        0                             \n*       Unknown     1270     1264        0       14                             \n*         TOTAL   230899   203020    19504      201    63240    54562     8677  \n*    \n*  App Summary  \n*                        Pss(KB)  \n*                         ------  \n*            Java Heap:     2708  \n*          Native Heap:    31448  \n*                 Code:    19788  \n*                Stack:      492  \n*             Graphics:   166356  \n*        Private Other:     1732  \n*               System:     8375  \n*    \n*                TOTAL:   230899       TOTAL SWAP PSS:      201  \n*    \n*  Objects  \n*                Views:        7         ViewRootImpl:        1  \n*          AppContexts:        2           Activities:        1  \n*               Assets:        2        AssetManagers:        2  \n*        Local Binders:       16        Proxy Binders:       21  \n*        Parcel memory:        5         Parcel count:       23  \n*     Death Recipients:        1      OpenSSL Sockets:        2  \n*             WebViews:        0  \n*    \n*  SQL  \n*          MEMORY_USED:        0  \n*   PAGECACHE_OVERFLOW:        0          MALLOC_SIZE:        0  <\/code><\/pre>\n\n\n\n<p> In contrast, executing the same command using an application featuring a full 3D Scene and a significantly higher amount of content prints the following information: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>* Applications Memory Usage (in Kilobytes):  \n* Uptime: 6823482422 Realtime: 10890859209  \n*   \n* ** MEMINFO in pid 22903 [com.unity3d.androidtest] **  \n*                    Pss  Private  Private  SwapPss     Heap     Heap     Heap  \n*                  Total    Dirty    Clean    Dirty     Size    Alloc     Free  \n*                 ------   ------   ------   ------   ------   ------   ------  \n*   Native Heap   304918   304900        0        0   327552   315885    11666  \n*   Dalvik Heap     1240     1096        0        0    11858     7127     4731  \n*  Dalvik Other      424      412        0        0                             \n*         Stack      528      528        0        1                             \n*        Ashmem        6        0        0        0                             \n*       Gfx dev   196934   132128        0        0                             \n*     Other dev        4        0        4        0                             \n*      .so mmap    23976      668    21920      199                             \n*     .apk mmap      368        0        0        0                             \n*     .dex mmap      116        4      112        0                             \n*     .oat mmap     6060        0     3768        0                             \n*     .art mmap     2774      604      332       25                             \n*    Other mmap       44        4        0        2                             \n*    EGL mtrack    21600    21600        0        0                             \n*     GL mtrack   384184   384184        0        0                             \n*       Unknown     6577     6568        0       17                             \n*         TOTAL   949997   852696    26136      244   339410   323012    16397  \n*    \n*  App Summary  \n*                        Pss(KB)  \n*                         ------  \n*            Java Heap:     2032  \n*          Native Heap:   304900  \n*                 Code:    26472  \n*                Stack:      528  \n*             Graphics:   537912  \n*        Private Other:     6988  \n*               System:    71165  \n*    \n*                TOTAL:   949997       TOTAL SWAP PSS:      244  \n*    \n*  Objects  \n*                Views:        7         ViewRootImpl:        1  \n*          AppContexts:        3           Activities:        1  \n*               Assets:        2        AssetManagers:        2  \n*        Local Binders:       15        Proxy Binders:       20  \n*        Parcel memory:        3         Parcel count:       14  \n*     Death Recipients:        0      OpenSSL Sockets:        0  \n*             WebViews:        0  \n*    \n*  SQL  \n*          MEMORY_USED:        0  \n*   PAGECACHE_OVERFLOW:        0          MALLOC_SIZE:        0  \n*<\/code><\/pre>\n\n\n\n<p>\n\nThe following table compares the results and describes the detailed stats:<\/p>\n\n\n\n<table class=\"wp-block-table\"><tbody><tr><td><strong>Area<\/strong><\/td><td><strong>Empty Scene [MB]<\/strong><\/td><td><strong>Full Scene [MB]<\/strong><\/td><td><strong>Description<\/strong><\/td><\/tr><tr><td>Pss<\/td><td>230<\/td><td>949<\/td><td>Proportional set size (Pss) is a metric the kernel computes that takes memory sharing into account. The system scales each page of RAM in a processby the ratio of the count of other processes using the same page. All private pages contribute 100% of their size, and shared memory contributes <strong>size\/(num of processes shared)<\/strong>. For example, a page that is shared between two processes will contribute half of its size to the Pss of each process. This way you can calculate the total RAM used by summing up the Pss across all processes. Comparing Pss between processes provides a rough idea of their relative weight.<\/td><\/tr><tr><td>Private Dirty<\/td><td>203<\/td><td>825<\/td><td>The most interesting and expensive metric is Private Dirty, which is the amount of RAM inside the process that cannot be paged to disk as it is not backed by the same data on disk, and the system cannot <strong>share<\/strong> with any other process. Another way to look at this is that this is the RAM that the system will reclaim when the application is destroyed. After reclaiming, it is quickly subsumed into caches and other uses because the system must fully utilize the limited memory available. <\/td><\/tr><tr><td>Native Heap<\/td><td>51<\/td><td>328<\/td><td>The Native Heap represents memory used by the process itself such as Unity Engine Code, Native C mallocs, and Mono VM.<\/td><\/tr><tr><td>Dalvik Heap<\/td><td>12<\/td><td>12<\/td><td>Dalvik Heap is memory the Dalvik VM allocates, for example; Variables in the Unity Java Android code.<\/td><\/tr><tr><td>Dalvik Other<\/td><td>0.4<\/td><td>0.4<\/td><td>Dalvik Other is memory used for JIT and Android GC.<\/td><\/tr><tr><td>Clean Memory<\/td><td>19<\/td><td>26<\/td><td>Android shares pages of memory among several processes such as code of common frameworks. As soon as memory in a page changes, the system must write to and modify the memory and flags the memory as dirty. However, clean memory is memory that hasn\u2019t changed from when it was loaded from disk. If a change occurs, the memory becomes dirty.<\/td><\/tr><tr><td>Swapped Dirty<\/td><td>0.2<\/td><td>0.2<\/td><td>The application uses Dirty memory as space for computations. Android does not have a swap mechanism so dirty memory is also RAM that will be freed when the app exits. However, Swapped Dirty is used on some Android devices with the ability to remap, but they swap to RAM rather than flash. On Android, this is similar to Linux. ZRAM can compress pages and the Linux kernel swaps them to a special RAM area and decompresses them again when needed.<\/td><\/tr><tr><td>EGL mtrack<\/td><td>99<\/td><td>22<\/td><td>This is gralloc memory usage. It&#8217;s primarily the sum of the SurfaceView and TextureView. It includes the frame buffer as well and therefore the size depends on the dimension of the framebuffers. The bigger the supported screen resolution, the higher the EGL mtrack number. In this test, the resolution of frame buffer for the full Scene was reduced to ensure good performance. Reducing the frame buffer size also reduces the amount of memory needed by these buffers. <\/td><\/tr><tr><td>GL mtrack &amp; Gfx dev <\/td><td>69<\/td><td>581<\/td><td>GL and Gfx are driver-reported GPU memory, and are primarily the sum of GL texture sizes, GL command buffers, fixed global driver RAM overheads, and Shaders. Note that this number does not appear on older Android versions. <strong>Note<\/strong>: The client space driver and kernel space driver share a memory space. In some Android versions this sometimes gets counted twice and therefore the Gfx dev is bigger than it is in reality.<\/td><\/tr><tr><td>Unknown<\/td><td>1.3<\/td><td>6.5<\/td><td>Unknown is any RAM page that the system could not classify into one of the other more specific items. This includes native allocations or runtime metadata, which the tool cannot identify when collecting this data due to Address Space Layout Randomization. Private Dirty is unknown RAM dedicated to only your application. <\/td><\/tr><\/tbody><\/table>\n\n\n\n<p>###&lt;a name=&#8221;procrank&#8221;>&lt;\/a> procrankOne alternative to dumpsys is <em>procrank,<\/em> another useful tool that you can use to view memory usage across all processes. It lists the memory usage of processes in order from highest to lowest usage. The sizes reported per process are Vss, Rss, Pss, and Uss.<em>~$ adb shell procrank<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>* PID      Vss      Rss      Pss      Uss  cmdline \n*  890   84456K   48668K   25850K   21284K  system_server \n* 1231   50748K   39088K   17587K   13792K  com.android.launcher2 \n*  947   34488K   28528K   10834K    9308K  com.android.wallpaper \n*  987   26964K   26956K    8751K    7308K  com.google.process.gapps \n*  954   24300K   24296K    6249K    4824K  com.unity.androidmemory \n*  888   25728K   25724K    5774K    3668K  zygote \n*  977   24100K   24096K    5667K    4340K  android.process.acore<\/code><\/pre>\n\n\n\n<ul><li>Vss &#8211; Virtual set size is the total accessible address space of a process. It shows how much virtual memory is associated with a process.<\/li><li>Rss &#8211; Resident Set Size is how many physical pages are allocated to the process. Pages shared between processes are counted multiple times.<\/li><li>Pss &#8211; Proportional Set Size takes the Rss number but evenly distributes shared pages among the sharing processes. For example, if three processes are sharing 9MB, each process gets 3MB in Pss.<\/li><li>Uss &#8211; Unique Set Size is also known as Private Dirty, which is basically the amount of RAM inside the process that cannot be paged to disk as it is not backed by the same data on disk, and is not shared with any other processes.<\/li><\/ul>\n\n\n\n<p><strong>Note:<\/strong> Pss and Uss are different than reports of <em>meminfo<\/em>. Procrank uses a different kernel mechanism to collect its data than meminfo which can give different results.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">meminfo<\/h1>\n\n\n\n<p>The meminfo command gives a summary of the overall memory usage of the system:<em>~$ adb shell cat \/proc\/meminfo<\/em>The first couple of numbers are worth discussing. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>* MemTotal:        2866492 kB  \n* MemFree:          244944 kB  \n* Buffers:           36616 kB  \n* Cached:           937700 kB  \n* SwapCached:        13744 kB<\/code><\/pre>\n\n\n\n<ul><li>MemTotal is the total amount of memory available to the kernel and userspace which is usually less than actual physical RAM as the handset requires memory for GSM, buffers, etc. as well.<\/li><li>MemFree is the amount of RAM that is not being used at all. On Android the number would typically be very small as the system tries to always use all available memory to keep processes running.<\/li><li>Cached is the RAM being used for filesystem caches etc.<\/li><\/ul>\n\n\n\n<p>For additional information please also read the <a rel=\"noreferrer noopener\" href=\"https:\/\/developer.android.com\/studio\/profile\/investigate-ram.html\" target=\"_blank\">RAM investigation page<\/a> and <a rel=\"noreferrer noopener\" href=\"https:\/\/developer.android.com\/topic\/performance\/memory.html\" target=\"_blank\">Android performance guides<\/a>. <\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Android Studio<\/h1>\n\n\n\n<p>Android Studio offers a memory profiler in addition to the command line tools available in the Android SDK. Similar to the command line tools reporting there is a split between managed and native memory.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/connect-prd-cdn.unity.com\/20190130\/9e72006d-5dc8-4e04-9c56-1a56141fcaf9_memory_management_in_unity_1.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>In this case, the table compares the empty Project from the <a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/best-practices\/android-memory-management#dumpsys%20meminfo\" target=\"_blank\" rel=\"noreferrer noopener\">dumpsys meminfo<\/a> section with the data from Android Studio. It basically covers the App Summary displayed from dumpsys meminfo with some additions.<\/p>\n\n\n\n<table class=\"wp-block-table\"><tbody><tr><td><strong>Section<\/strong><\/td><td><strong>Size [MB]<\/strong><\/td><td><strong>Area<\/strong><\/td><\/tr><tr><td>Total [mb]<\/td><td>168.7<\/td><td>All <\/td><\/tr><tr><td>Others [mb]<\/td><td>3.1<\/td><td>Other dev + Unknown<\/td><\/tr><tr><td>Code [mb]<\/td><td>28<\/td><td>mmaps<\/td><\/tr><tr><td>Stack [mb]<\/td><td>0.1<\/td><td>Stack<\/td><\/tr><tr><td>Graphics [mb]<\/td><td>88.7<\/td><td>Gfxdev + EGL mtrack + GL mtrack <\/td><\/tr><tr><td>Native [mb]<\/td><td>40.8<\/td><td>Native Heap<\/td><\/tr><tr><td>Java [mb]<\/td><td>8<\/td><td>Dalvik Heap<\/td><\/tr><\/tbody><\/table>\n\n\n\n<h1 class=\"wp-block-heading\">Plugins<\/h1>\n\n\n\n<p>Usually, most of the memory goes into the Native Heap section. The Dalvik Heap is small compared to the Native Heap section. In case it grows, you should investigate the Android plugins you use in your application. The Native Heap makes it difficult to know where memory comes from and there is no great way to see Native Plugin allocations in the profiler. A possible solution to gain a greater insight is to isolate and measure the plugins used for 3rd party integrations and compare them with the memory baseline of an empty Project.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6.Application Size<\/h2>\n\n\n\n<p>One way you can save disk space and runtime memory is to reduce the size of your .apk on Android or .ipa on iOS. Resources and code are directly proportional to runtime memory and if you can reduce them, you can save runtime memory. Please read <a href=\"https:\/\/unity3d.com\/learn\/tutorials\/topics\/best-practices\/il2cpp-mono#Code%20stripping%20in%20Unity\" target=\"_blank\" rel=\"noreferrer noopener\">Code stripping in Unity<\/a> to learn more about reducing code size and read <a href=\"https:\/\/support.unity3d.com\/hc\/en-us\/articles\/208412186-IL2CPP-build-size-optimizations\" target=\"_blank\" rel=\"noreferrer noopener\">this knowledge base article<\/a> if you want to understand the details of IL2CPP optimization on iOS.\n\n<\/p>\n\n\n\n<p><br><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Proper memory management in Unity can be challenging. T [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[18],"_links":{"self":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/2181"}],"collection":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2181"}],"version-history":[{"count":2,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/2181\/revisions"}],"predecessor-version":[{"id":2192,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/2181\/revisions\/2192"}],"wp:attachment":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2181"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2181"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2181"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}