Memory Leak w/ Scripted Importer and NativeArray in Editor

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:

[PreferBinarySerialization]
public class MyTree : ScriptableObject
{
    public uint[] Data = null;

    private NativeArray<uint> _jobData;

    private void OnEnable()
    {
        if (Data != null)
            _jobData = new NativeArray<uint>(Data, Allocator.Persistent);
    }

    private void OnDisable()
    {
        if (_jobData.IsCreated)
            _jobData.Dispose();
    }
}

This data is intented to be immutable (will not change once imported unless re-imported).

MyTreeis 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):

  • Upon Deleting the Asset
  • Upon Applying new Import Settings
  • Upon Right-Click>Reimport
  • Upon Changing ScriptedImporterversion (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

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:

  • Overriding OnWillDeleteAsset in an AssetModificationProcessor (Unload and return AssetDeleteResult.DidNotDelete)

  • Overriding Apply in the ScriptedImporterEditor (Unload before calling base.Apply)

  • Overriding the context menu item as outlined here (Unload then call AssetDatabase.Import)

  • I have no idea if this will work or not

  • 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!

1 Like