Search Unity

Question What are all the "native engine object" types in Unity?

Discussion in 'Documentation' started by Interjection, Jun 22, 2020.

  1. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    This thread is concerning the need to manually dispose of stuff not collected by the Garbage Collector. Hopefully this thread will show up for other people searching for what "native engine object" means.

    It says on this page: https://docs.unity3d.com/ScriptReference/RenderTexture.Release.html
    >As with other "native engine object" types, it is important to pay attention to the lifetime of any render textures and release them when you are finished using them, as they will not be garbage collected like normal managed types."

    However, there is no link to a list of exactly what other "types" I need to manually manage.

    If I click "Memory" inside Unity's profiler tab the following is mentioned:
    Textures, Meshes, Materials, AnimationClips and AudioClips

    Is that it? Will the Garbage Collector take care of everything else except for these five categories?

    "Textures" I assume is Texture2D and RenderTexture.
    "Meshes" I assume is Mesh but NOT the MeshFilter component.
    "Materials" I assume is anything created with new Material(Shader.Find("..."));
    Same with new AnimationClip(); and AudioClip.Create(); (or however you are supposed to create new AudioClips these days).

    I also assume this only applies to these things if specifically created in MY code. For example, when calling "r.material.color = Color.white;" (where r is a Renderer) I expect the Renderer to take care of disposing the Material copy it automatically creates. I also assume anything created with Resources.Load(); won't need manual disposal.

    Also, exactly how are we supposed to dispose of these things? RenderTexture has a Release() method, Texture2D does not. I assume GameObject.Destroy(tex2d) will take care of it? Do I also need to do the same for RenderTexture after calling Release()?

    Am I missing something? What other "native engine object" types are there? Some? None? Maybe one of the above isn't one?

    Finally, I suggest Unity adds a title on this page that brings up "native engine object" types and explains that you cannot always rely on the GC to take care of everything:
    https://docs.unity3d.com/Manual/UnderstandingAutomaticMemoryManagement.html

    As a bonus I'll mention that I think there is some cleanup of "native engine objects" when you change the scene in your game? Correct me if I'm wrong, I didn't get it from the official docs. If you never change scene or stay on the same one for a long time it won't help, though.
     
  2. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,461
    Hi,

    Thanks for pointing out this gap in the docs. While we work on fixing that, I'll try to answer the questions you raised here.

    Native Unity Objects means any type that inherits from UnityEngine.Object. Any such type can have a Managed Wrapper/Shell object used to communicate with the native side from a script. That wrapper can get garbage collected, but that won't cause the destruction of the native Object.

    Native Objects can either be
    1. loaded in with a scene or associated with it at runtime (i.e. instantiating Game Objects get placed in the currently active scene) and will get unloaded on the second destructive (i.e. non Additive) scene load, or once you call Resources.UnloadUnusedAssets after that scene is unloaded.
    2. Made persistent via DontDestroyOnLoad
    3. Created with new
    4. accessing the .material property (instead of .sharedMaterial)
    5. Instantiating ScriptableObjects (or anything but a GameObject, really)
    6. Creating Textures/Render Textures and other Asset types
    Now, everything but 1. requires custom handling of it's life time, i.e. you'll need to call Object.Destroy on it to clean it up.

    AddComponent will associated the object the the GameObject you add it too so that's gonna be the thing it gets unloaded with, or not.

    Now the second destructive scene unload thing: Every destructive scene unload, to ensure there's space to load to and no too old Memory dangling around, triggers a Resources.UnloadUnusedAssets, before unloading, assuming some assets might still be used in the next scene, and unloading them would be wasting resources. You can always call it manually but if you only have one scene, everything that was loaded in with that scene will permanently stay in memory, yes (except for all the on the fly stuff that was instantiated and destroyed obviously).
     
  3. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    OK so if I look at for example: https://docs.unity3d.com/ScriptReference/Mesh.html
    And see that it says "Inherits from:Object", then it means I must clean it up myself? (AnimationClip inherits from Motion, which inherits from Object, so I assume that also means AnimationClip must be cleaned up.)

    What's the best practice of cleaning up all these Object instances? I assume OnDestroy() if I create them inside a MonoBehaviour but what about non-MonoBehaviour classes? I tried a little with Finalizers but they are unreliable at best.

    Should I use a SafeHandle? I see that both Texture2D and RenderTexture have GetNativeTexturePtr() and Mesh has GetNativeIndexBufferPtr(), to get an IntPtr object. But AudioClip or AnimationClip does not have anything like that.

    Do I need to call renderTexture.Release() before I call GameObject.Destroy(renderTexture) or is it enough to just call GameObject.Destroy(renderTexture)?

    It's strange to hear that a Material created by accessing the .material property is NOT cleaned up by the Renderer when the Renderer itself is destroyed. I bet most Unity developers doesn't know that. You should put a warning about potential memory leak in the summary of the XML doc for the .material property. Right now it just says: "Returns the first instantiated Material assigned to the renderer." I was 100% sure the Renderer took care of any Material it creates.
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Honestly, .material should be deprecated. It's not very hard to do renderer.sharedMaterial = Instantiate(renderer.sharedMaterial) when you need the behaviour.

    It's pretty well documented what the property does. Unity's for some reason very sparse when it comes to putting documentation in the xml, not sure why.
     
    Maeslezo likes this.
  5. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,461
    Yes but as I mentioned only IF these didn't get loaded in with a Scene. Otherwise the unloading the scene and UnloadeUnusedAssets will take care of it.

    As for best practices regarding lifetime handling: it really depends on what the lifetime is in every case. If the object should survive until the MonoBehavior that created it is destroyed, then yes, Ondestroy sounds like a good place. If it is created in OnEnable instead of OnAwake or OnStart or some other, once in it's lifetime call, then you might want to use OnDisable or similar instead.

    I'm not sure what the point would be of keeping some native pointers around, when just keeping the reference to the managed wrapper object is essentially the same as keeping that pointer to native and simultaneously keeping the native resource alive, i.e from being unloaded unless something else is actively calling Destroy() on it. You can always use if(manageObjectWrapper) to see if the native resource has been Destroyed, i.e. that the pointer used in the wrapper is still valid.

    As for the XML doc. Not too sure myself but that sounds like only the summary, i.e. usually the first line, would be included there. No idea what the reasoning for that is.

    No idea what all the reasons would be to keep .material but what you, @Baste , suggested is not the same as you'd change the material for all Renderers using the same material... I assume there's some complex reasons for keeping it but I agree that it is a terrible pitfall.
     
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    No! Setting the shared material to something doesn't affect any other renderers.

    The name might imply that it does something like that, and the docs are poorly worded, but actually .sharedMaterial works exactly like you'd expect any reference to any asset to do. You can test it yourself:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. // Make a bunch of copies of a sphere or whatever, and slot this script on one of them. Only that one will change color.
    5. public class TestScript : MonoBehaviour {
    6.  
    7.     private void Start() {
    8.         var mr = GetComponent<MeshRenderer>();
    9.  
    10.         mr.sharedMaterial = Instantiate(mr.sharedMaterial);
    11.         mr.sharedMaterial.color = Color.red;
    12.     }
    13. }

    It's just .material that's weird and idiosyncratic since it works around a common mistake that it's easy to understand and fix by potentially introducing hard-to-debug performance issues instead.
     
  7. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,461
    Whoops, sorry about that. Should've checked first. I take that back then...
     
  8. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    What I meant with "but what about non-MonoBehaviour classes" in the post above is:
    How should I best deal with them outside of MonoBehavior? I have classes that doesn't inherit from anything, just C#'s object (not Unity's Object). These classes call "new Mesh()", "new Material()" or "new RenderTexture()".

    My solution for the moment is to have a manager that keeps a reference to the owner of the Mesh/Material/texture inside a WeakReference, that way I can either manually ask the manager to clean up after I get rid of an owner or the manager can discover that there are no more references left to the owner and then dispose of the Mesh/Material/texture. (I assume C# still garbage collects my classes that doesn't inherit from anything.)

    Please answer if I need to call Release() on my RenderTexture before I destroy it. Or if Object.Destroy() takes care of it.
     
  9. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    I was also wondering whether or not it's important for me to set hide flags on things created from C#, like for example:

    Material mat = new Material(Shader.Find("..."));
    mat.hideFlag = HideFlags.HideAndDontSave; //is this needed?

    Since these Materials/Meshes/Textures are already managed by me and doesn't come from Resources.Load() or were included at scene creation they shouldn't be affected by calls to Resources.UnloadUnusedAssets(), right? How would Resources know that it exists?

    It says on https://docs.unity3d.com/ScriptReference/Resources.UnloadUnusedAssets.html the following:
    >The script excecution stack, however, is not examined so an asset referenced only from within the script stack will be unloaded and, if necessary, loaded back in the next time one of its properties or methods is used.

    I assume that text in the documentation only applies to things not created with "new"?

    If Material/Mesh/Texture2D/RenderTexture etc created with "new" is affected by UnloadUnusedAssets() --I assume they are not affected-- I'd like to also ask: Is there a benefit to NOT set the hideFlag on manually managed things created in C#?
     
  10. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    There's very few reasons to set hide flags at runtime, unless you're setting the DontUnloadUnusedAsset flag, or it's a gameObject you want to HideInInspector.

    For RenderTextures, Release and Destroy does different things. I don't know if Destroying it also Releases it, can't tell from the docs.

    I'm also pretty sure that Meshes and whatnot you new() up does get removed by UnloadUnusedAssets. My impression there was that the native side-version (ie. the c++ engine version) got freed by that method if there's not any C# references to it left, but I might be wrong.
     
  11. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,461
    Yes if all managed references to the Native UnityEngine objects got collected by the managed GC, the Asset GC that you trigger with Resources.UnloadUnusedAssets will unload them.

    Pre 2017.4 or something yes, 2017.4 and up, Destroy calls Release if needed.
     
    ARon_w likes this.
  12. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    Okay, good to know that Destroy() also calls Release(). I assume this is the same for other stuff that needs to be cleaned up manually, that only Destroy() needs to be called and it takes care of calling other necessary methods.

    This is confusing. Now it sounds like I just need to call UnloadUnusedAssets() instead of managing the destruction of textures or meshes myself. Even if I made the texture/mesh from "new". Sounds like what you said before, that everything inheriting from Object needs manual cleanup isn't true (a Native Object was for example "Creating Textures/Render Textures and other Asset types").

    Mixing manually managed memory into a garbage-collection language like this but not explicitly having a list of what classes to watch out for is strange. Having to set hideFlag is strange, talking about managed or unmanaged is strange. Everything is so unclear.
     
    Maeslezo and david-wtf like this.
  13. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    I haven't ever used HideFlags for memory management. I've never heard of anyone doing that either. I suspect it's very much a narrow edge-case that you shouldn't worry about for now.

    So this is very much dependent on your game. If you change levels often, you can probably stick a Resources.UnloadUnusedAssets into the load screen and never care. If you don't have load screens or other times where it's fine that the game hangs for ~.1 seconds, you'll want to manually Destroy the resources.
     
  14. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    @Baste: I never change level (scene) and I have no load screens (instead I load things as they are needed, preferably off the main-thread when I can).

    What Unity should do is to give us a list of exactly which classes we need to watch out for when creating them from scratch in C#. With "from scratch" I mean without calling Resources.Load(). Saying "everything that inherits from UnityEngine.Object" and then implying that meshes made with new Mesh() gets automatically cleaned up doesn't cut it for clarity. Maybe I misunderstood MartinTilo but if I'm not smart enough to understand him maybe there are more like me that would appreciate a list of classes to watch out for.

    Until such a list is made I will continue to assume that I need to manually clean up everything that inherits from UnityEngine.Object, except GameObject or things created by calling Resources.Load().

    And I will also continue to assume that calling UnityEngine.Object.DestroyImmediate() takes care of all the cleaning, which includes calling other methods such as Release() on RenderTexture. Please correct me if I'm wrong, of course.
     
    david-wtf likes this.
  15. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    There's a pretty decent memory profiler that ships with the game. You can use that to see if you have actual issues around dangling objects.
     
  16. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    @Baste: I use it extensively but in this case that's more of a workaround for not knowing what is going on. I would much prefer to have a list of classes to watch out for than having to sit through trial and error sessions or having to do guesswork.
     
  17. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    If I call
    UnityEngine.Shader s = UnityEngine.Shader.Find("...");

    do I have to manually clean up s by calling
    UnityEngine.Object.DestroyImmediate(s);

    before application exit or before exiting playmode in the editor?

    Or does UnityEngine.Shader take care of it? The Shader is not used by any Materials that exists before pressing play, everything is created in code.
     
  18. david-wtf

    david-wtf

    Joined:
    Sep 30, 2021
    Posts:
    25
    Chiming in here too (more than a year after the original conversation). I'm new to Unity game development, coming from the Android app ecosystem where interoperability between a GC-managed heap and an unmanaged heap exists, too. So naturally, I was interested in details about memory management in Unity, too.

    I looked at the `RenderTexture` API reference, and noticed that there are a couple of cleanup-related methods (i.e. `Release()` and `DiscardContents()`) and started wondering if and when I should use those. I then noticed the "native engine objects" mentioning in the docs, which ultimately brought me here.

    After reading this conversation, my understanding is not a lot better - and I'm still curious to understand the details. I haven't used the Unity profiler yet (will do soon to explore this myself), but I think the documentation should touch the memory-management topic explicitly - as this is an integral part of the Unity game engine that every developer has to deal with. Has there been any update on the documentation side of things since this discussion was sparked?
     
    sking995 likes this.
  19. Neto_Kokku

    Neto_Kokku

    Joined:
    Feb 15, 2018
    Posts:
    1,751
    Nope. This is one of the many reasons Unity is known as being all rainbows and flowers when starting a game but becoming hell towards the end.

    There are several gotchas involving memory management in Unity which are vaguely documented, of which most people will learn when they are knee deep in performance and memory issues at the end of the project.

    C# and its garbage collector are deceptively presented as freeing developers from having to manually manage memory, but that's not true for any type which comes from Unity's C++ side.

    If your game has hard level transitions, it's mostly taken care for you due to Resources.UnloadUnusedAssets, but if you want seamless streaming you are in for a world of pain, suffering, and endless bizarre workarounds. Unity is simply not designed for that and the few games which achieve it often resort to source code modification.
     
    david-wtf likes this.
  20. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,461
    Well, yes but that is still ongoing. The first batch of new documentation regarding Memory Management in Unity has been placed in this new section of the Manual under Working in Unity > Analysis > Memory in Unity. That was finalized a month or so ago iirc.
    The topic is deeply technical and writing documentation for it quite tricky and slow going for several reasons. A good chunk of them being that it deals with otherwise hidden systems and native code and concepts that are quite abstract from the more tangible and easier to understand surface level elements of the engine. Going deep enough to explain these but keeping it relevant, relatable and actionable with regards to normal everyday use of the engine is a tricky balance to strike.

    The 2021.2 related Memory allocator customization documentation is probably the deepest we've gone yet in that regard and likely too deep for most users. Since these allocator names do show up in the Memory Profiler Package when looking at e.g. the Fragmentation page or All Native Allocations Table, they are getting more exposure and having that documentation in place will likely help some to understand what they are looking at in these tools in a deeper way.

    Regarding the UnityEngine.Object inheriting types: I am actively drafting an extension to the current Memory in Unity section that will go deeper on these and will likely be far less abstract than the allocator customization page. It is also very relevant to an upcoming feature for the Memory Profiler Package so, there's some current traction behind that particular bit of documentation and combined with the tool's update, will likely contribute to a far better understanding of the mechanics behind this all.

    If there are any particulars that are unclear to you, I can try to clear them up here and make sure that these points are covered in the upcoming documentation update.
     
  21. sking995

    sking995

    Joined:
    Mar 22, 2021
    Posts:
    26
    Unity has some great memory management that protects us from memory leaks, however there are still issues that I've ran into where it can be hard to find documentation about.

    For example, we used a lot of RenderTextures in a project and were destroying them, however when using the new memory profiler, we noticed they were still sitting, un-referenced, in memory. It's odd to me that you would need to call release on it before destroying it, as you would think it would be called OnDestroy from within itself. Same goes for Renderer.material. Maybe there are good reasons for this, I dunno.

    I too came across this thread when trying to write a memory management article for my company :,D
     
  22. david-wtf

    david-wtf

    Joined:
    Sep 30, 2021
    Posts:
    25
    I totally understand that, and can see that writing a general-purpose documentation article for all audiences is hard to do. I'd suggest writing an in-depth / under-the-hood blog post, though, for all the game devs and software engineers who want to understand what happens behind the scenes. This doesn't have to be an evergreen (as I expect that things often change below the surface) but could be a great way for interested developers to learn more about how things work. This would be a win-win, too, since the community could then start answering those nitty-gritty detail questions itself.

    Amazing! I haven't read that one yet, but will check it out right away. Thanks a lot

    Definitely a hero right here! Thanks a lot - I really appreciate the effort.