Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Help Wanted 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:
    47
    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:
    803
    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:
    47
    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:
    4,940
    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.
     
  5. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    803
    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:
    4,940
    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:
    803
    Whoops, sorry about that. Should've checked first. I take that back then...
     
  8. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    47
    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:
    47
    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:
    4,940
    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:
    803
    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.
     
  12. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    47
    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.
     
  13. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,940
    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:
    47
    @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.
     
  15. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,940
    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:
    47
    @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:
    47
    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.
     
unityunity