Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Memory Leak w/ Scripted Importer and NativeArray in Editor

Discussion in 'Entity Component System' started by AnthonyReddan, Jul 7, 2020.

  1. AnthonyReddan

    AnthonyReddan

    Joined:
    Feb 27, 2018
    Posts:
    39
    Hi there,

    I'd like to use jobs in the editor (i.e. at edit time, will also be used at runtime), and it seems like it should be totally supported as mentioned here. I'm creating and disposing of a
    NativeArray
    (for use in said jobs) like so:

    Code (CSharp):
    1. [PreferBinarySerialization]
    2. public class MyTree : ScriptableObject
    3. {
    4.     public uint[] Data = null;
    5.  
    6.     private NativeArray<uint> _jobData;
    7.  
    8.     private void OnEnable()
    9.     {
    10.         if (Data != null)
    11.             _jobData = new NativeArray<uint>(Data, Allocator.Persistent);
    12.     }
    13.  
    14.     private void OnDisable()
    15.     {
    16.         if (_jobData.IsCreated)
    17.             _jobData.Dispose();
    18.     }
    19. }
    20.  
    This data is intented to be immutable (will not change once imported unless re-imported).

    MyTree
    is created (and its data array filled out) via custom
    ScriptedImporter
    . On the first import, and in regular use,
    OnEnable/OnDisable
    are called exactly as expected. However, there are a few cases where
    OnDisable
    is never called (and we therefore have memory leaks):
    1. Upon Deleting the Asset
    2. Upon Applying new Import Settings
    3. Upon Right-Click>Reimport
    4. Upon Changing
      ScriptedImporter
      version (untested)
    In the first two cases, I can catch them before they happen and dispose manually (i.e.
    OnWillDeleteAssets
    and overriding
    Apply
    in the
    ScriptedImporterEditor
    ). However, I'm stumped at what to do for Reimports. I've tried hooking into
    OnPreProcessAsset
    but its too late - the old scriptable is already destroyed at this point (
    AssetDatabase.LoadMainAssetAtPath(assetPath) 
    returns
    null
    ).

    Does anybody have any ideas how I can get a callback or hook into right before a reimport happens on an asset so I can dispose of the
    NativeArray
    data properly? Or perhaps I need a different way of thinking about how to allocate my
    NativeArray 
    for use in jobs in both editor and runtime?

    Thanks,
    Ant
     
    Last edited: Jul 8, 2020
  2. AnthonyReddan

    AnthonyReddan

    Joined:
    Feb 27, 2018
    Posts:
    39
    I've thought about this some more and I think I may try to dispose the
    NativeArray
    by unloading (
    Resources.UnloadAsset
    )
    MyTree
    right before each of these events occur. This would be:
    1. Overriding
      OnWillDeleteAsset
      in an
      AssetModificationProcessor
      (Unload and return
      AssetDeleteResult.DidNotDelete
      )
    2. Overriding
      Apply
      in the
      ScriptedImporterEditor
      (Unload before calling
      base.Apply
      )
    3. Overriding the context menu item as outlined here (Unload then call
      AssetDatabase.Import
      )
      • I have no idea if this will work or not
    4. Subscribing to
      AssemblyReloadEvents.beforeAssemblyReload
      and Unload the asset
      • Scriptables are already unloaded at this point, the problem is that they are Loaded again once the assembly is reloaded (which when you change the
        ScriptedImporter
        version, is right before it triggers re-import). By plugging into this I'm hoping to prevent Unity from adding the Scriptable to some 'Reload' queue once the assembly reload is complete..
      • This solution is also somewhat unfortunate as in most cases I'd want
        MyTree
        to be reloaded once scripts are reloaded
    Ideally, Unity would just Unload ScriptableObject before destroying them outright in each of these cases and I'd have my OnDisabled call for free!
     
    Last edited: Jul 8, 2020
    alexeyzakharov likes this.