Managed code stripping is a critical step in the build process that helps decrease the size of an application’s binary files. This occurs through the removal of unused code.
By removing code, you ensure that it won’t be compiled or included in the final build. While this should slightly reduce memory usage for projects running with the IL2CPP backend, there’s always the risk of missing types and methods at runtime (among other issues) with higher managed code stripping levels.
Throughout the build process, some code is considered unused, and consequently, stripped down. Manually adding needed assemblies to the link.xml file might not be the simplest approach to preserve them from removal. During project reviews that I conduct, as part of my work as a Unity Software Development Consultant, I’ve received questions from customers on how they can better handle managed code stripping. That’s why I’ve gathered these tips and best practices which may improve your workflow with support from new managed code stripping annotation attributes.
The removal of unused code is especially important when using the IL2CPP scripting backend. The Unity linker, a version of the Mono IL linker customized to work with Unity, performs a static analysis to strip managed code.
Unity supports three levels of managed code stripping for IL2CPP – Low, Medium, and High. The managed code stripping manual explains how the code stripping process works, which factors strip down certain code, and how stripping levels differ from each other. In short: The higher the code stripping level, the harder the linker tries to find and remove the unused code. You can modify the Managed Stripping Level in your project’s Player Settings.
Static analysis, leveraged by the linker for the identification of unused code, cannot cover all cases when a certain object’s type is only defined at runtime. These cases lead to false-positive results. Though there might not be any reference to a class or method while compiling, the class or method is still required by some parts of the code at runtime. In this context, the code that uses Reflection serves as a good example. Consider the following snippet:
While this is a valid and commonly used type of code, the linker doesn’t know whether MyAssembly, MyType, and MyMethod are actually used at runtime. This can cause them to be stripped down, and by extension, result in a runtime error. Check out the stripping restrictions manual for more information.
Developers that use Dependency Injection frameworks like Zenject, or serialization libraries like Newtonsoft.Json, have to be aware that false-positive code stripping is a possibility, and should address it accordingly. Here are some of the most common approaches:
Even the Addressables package harnesses the LinkXmlGenerator. The Addressables’ build script reviews the list of assets in the groups and adds types used by assets into the link.xml file. It also adds in types used by Addressables internally via Reflection at runtime. Consider reviewing the default build script, BuildScriptPackedMode.cs, for more details on implementing a similar solution as a step in your build process, like with the Scriptable Build Pipeline.
Unity supports multiple link.xml files located inside of the Assets folder or one of its subfolders. During the build process, entries of multiple link.xml files are merged and considered by the linker.
Using the [Preserve] attribute might require some manual work. But if your project is already on Unity 2020 LTS, you can use a number of new managed code stripping annotation attributes to easily and precisely mark assemblies, classes, and methods that shouldn’t be removed during code stripping. Here are just some of them:
In Unity 2020.1 and 2020.2, the tool received API updates to match Mono IL linker. It can now detect some simple reflection patterns, which means that if you’ve upgraded to Unity 2020 LTS, you have fewer reasons to use link.xml files.
As part of our 2021 goal of making it easier for you to deliver high-quality builds to your testers and players, we’ve stayed focused on improving code stripping workflows. More specifically, we’ve added a new Managed Stripping Level called Minimal to the 2021.2 release. This will be the default for the IL2CPP backend, so be sure to stay tuned.