Search Unity

I need some help disposing of a NativeArray... [Solved]

Discussion in 'Entity Component System' started by NotaNaN, Dec 16, 2018.

  1. NotaNaN

    NotaNaN

    Joined:
    Dec 14, 2018
    Posts:
    325
    I'm really sorry guys for wasting your time on this, but i can't for the life of me dispose of this dang array without getting an error message! So if you wouldn't mind throwing me a lifeline (so i can finally get past this roadblock) then i would be absolutely overjoyed.

    Here's my error:
    A Native Collection has not been disposed, resulting in a memory leak.

    I'm sure we all have seen it before... Nothing new -- nothing fancy.

    Here's all of the things I've attempted to do to rid myself of it:
    Dispose of the array in the OnDestroyManager event, like so:
    Code (CSharp):
    1. protected override void OnDestroyManager()
    2. {
    3.     positionArray.Dispose();
    4. }
    Honestly i didn't think that would work... And even if it did there is still technically a memory leak due to never deallocating the array until the entity is destroyed.
    Add the "DeallocateOnJobCompletion" modifier to the NativeArray during instantiation, just like this:
    Code (CSharp):
    1. [DeallocateOnJobCompletion] NativeArray<Vector2> positionArray;
    To my surprise (and disappointment), absolutely nothing changed. *Sigh*, back to testing...
    Re-formatting the job scheduling to allow me to place the .Dispose() between the aforementioned scheduling of the job and the return statement:
    Code (CSharp):
    1. MovementJob job = new MovementJob
    2. {
    3.     RigidbodyPositionArray = positionArray,
    4. };
    5. var movementJobSchedule = job.Schedule(_data.Length, 64, inputDeps);
    6. movementJobSchedule.Complete();
    7.      
    8. -> I tried to dispose of the array right here <-
    9.  
    10. return inputDeps;
    This time i totally thought it would work -- but as expected, it didn't. Actually, all it did was make unity throw more error messages at me... Hence attempt No.4.
    I then tried to dispose of the array in as many areas as i could think of... Or more accurately, as many areas as Unity would let me. Obviously i didn't succeed with doing this, if i did i wouldn't be here.

    Sadly, that's my 6+ hours of researching how to dispose of a array compiled into a two minute sob-story... So, I hope you liked it (or at least found it to be interesting).

    Also, here's a TL;DR for those of you who don't like reading or just want the thread summarized:
    Where are you supposed to dispose of a NativeArray -- and how should you do it?

    Thanks guys in advance for any and all help with this, it really is appreciated.
     
    florianhanke likes this.
  2. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Show where you got that NativeArray and then we can talk!
    e.g. if you allocated persistent on OnCreateManager, then approach no.1 certainly works. I have multiple of that in my game.
     
  3. NotaNaN

    NotaNaN

    Joined:
    Dec 14, 2018
    Posts:
    325
    Pardon my unfortunately slow reply -- there were some unforeseen delays, but they're resolved now.
    Normally i would have edited the main post, but it appears that the forums wouldn't let me due to it being counted as spam. Either way, here is the code.
    Code (CSharp):
    1. public class PlayerMovementSystem : JobComponentSystem
    2. {
    3.     //Here's where i instantiate it
    4.     [DeallocateOnJobCompletion] NativeArray<Vector2> positionArray;
    5.  
    6.     protected struct MovementJob : IJobParallelFor
    7.     {
    8.         public int Length;
    9.         public EntityArray Entities;
    10.         public ComponentArray<Rigidbody2D> Rigidbodys;
    11.         public NativeArray<Vector2> RigidbodyPosition;
    12.         public ComponentArray<PlayerInputComponent> PlayerInputComponent;
    13.         public ComponentDataArray<MovementComponent> PlayerMovementComponent;
    14.  
    15.         NativeArray<Vector2> positionArray;
    16.  
    17.         public float DeltaTime;
    18.  
    19.         public void Execute(int index)
    20.         {
    21.             var moveVector = new Vector3(PlayerInputComponent[index].Horizontal, 0, PlayerInputComponent[index].Vertical);
    22.  
    23.             var movePosition = RigidbodyPosition[index] + (Vector2)moveVector.normalized * 3 * DeltaTime;
    24.  
    25.             //Rigidbodys[index].MovePosition(movePosition);
    26.  
    27.             //transform[index].position = new Vector3 (RigidbodyPosition[index].x, RigidbodyPosition[index].y, 0);
    28.         }
    29.     }
    30.  
    31.     private struct Data
    32.     {
    33.         public readonly int Length;
    34.         public EntityArray Entities;
    35.         public ComponentArray<Rigidbody2D> Rigidbodys;
    36.         public ComponentArray<PlayerInputComponent> PlayerInputComponent;
    37.         public ComponentDataArray<MovementComponent> PlayerMovementComponent;
    38.     }
    39.  
    40.     [Inject] private Data _data;
    41.  
    42.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    43.     {
    44.         var deltaTime = Time.deltaTime;
    45.         //Here the size and allocator type is set
    46.         positionArray = new NativeArray<Vector2>(_data.Rigidbodys.Length, Allocator.TempJob);
    47.      
    48.         //It is then written to...
    49.         if (_data.Rigidbodys.Length > 0)
    50.         {
    51.             for (int i = 0; i < _data.Length; i++)
    52.             {
    53.                 positionArray[i] = _data.Rigidbodys[i].position;
    54.             }
    55.         }
    56.    
    57.         //And finally used in the MovementJob
    58.         MovementJob job = new MovementJob
    59.         {
    60.             Length = _data.Length,
    61.             Entities = _data.Entities,
    62.             Rigidbodys = _data.Rigidbodys,
    63.             RigidbodyPosition = positionArray,
    64.             PlayerInputComponent = _data.PlayerInputComponent,
    65.             PlayerMovementComponent = _data.PlayerMovementComponent,
    66.             DeltaTime = deltaTime,
    67.         };
    68.         var movementJobFence = job.Schedule(_data.Length, 64, inputDeps);
    69.         movementJobFence.Complete();
    70.         return inputDeps;
    71.     }
    72. }
    73. //Notice: I have excluded all of my positionArray.Dispose(); calls to reduce clutter
    74.  
    Hmm, a persistent OnCreateManager you say? I'll look into that... To tell you the truth i barely know what the OnDestroyManager does -- so i suppose some more research is in order.
    (Also, apologies for not originally including the code sample -- it seems kinda important in hindsight... :I)
     
    Last edited: Dec 16, 2018
  4. Attatekjir

    Attatekjir

    Joined:
    Sep 17, 2018
    Posts:
    23
    The [DeallocateOnJobCompletion] tag is to be added to the NativeArray in the job, not outside of the job. This tag will deallocate the NativeArray for you when the job is completed. Like so:

    Code (CSharp):
    1. public class MovementSystem : JobComponentSystem
    2. {
    3.     NativeArray<Vector2> positionArray;
    4.  
    5.     public struct MovementJob : IJob
    6.     {
    7.         [DeallocateOnJobCompletion]
    8.         public NativeArray<Vector2> positionArray;
    9.  
    10.         public void Execute()
    11.         {
    12.  
    13.         }
    14.     }
    15.  
    16.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    17.     {
    18.         positionArray = new NativeArray<Vector2>(3, Allocator.TempJob);
    19.  
    20.         MovementJob job = new MovementJob
    21.         {
    22.             positionArray = positionArray,
    23.         };
    24.  
    25.         var movementJobSchedule = job.Schedule(inputDeps);
    26.  
    27.         return movementJobSchedule;
    28.     }
    29. }


    Another way is disposing a created NativeArray in the next update. Do not forget to then also dispose the NativeArray in OnDestroyManager.

    Code (CSharp):
    1. public class MovementSystem : JobComponentSystem
    2. {
    3.     NativeArray<Vector2> positionArray;
    4.  
    5.     public struct MovementJob : IJob
    6.     {
    7.         public NativeArray<Vector2> positionArray;
    8.  
    9.         public void Execute()
    10.         {
    11.  
    12.         }
    13.     }
    14.  
    15.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    16.     {
    17.         if (positionArray.IsCreated)
    18.         {
    19.             positionArray.Dispose();
    20.         }
    21.         positionArray = new NativeArray<Vector2>(3, Allocator.TempJob);
    22.  
    23.         MovementJob job = new MovementJob
    24.         {
    25.             positionArray = positionArray,
    26.         };
    27.  
    28.         var movementJobSchedule = job.Schedule(inputDeps);
    29.  
    30.         return movementJobSchedule;
    31.     }
    32.  
    33.     protected override void OnDestroyManager()
    34.     {
    35.         base.OnDestroyManager();
    36.         if (positionArray.IsCreated)
    37.         {
    38.             positionArray.Dispose();
    39.         }
    40.     }
    41. }


    Note that other native containers do not have the [DeallocateOnJobCompletion] attribute, but by using the second method you are still able to dispose of them efficiently.

    Also i think your attempt No.3 should of worked, maybe there are additional mistakes made somewhere else in your code.
     
    Last edited: Dec 16, 2018
    CAMBCN and NotaNaN like this.
  5. NotaNaN

    NotaNaN

    Joined:
    Dec 14, 2018
    Posts:
    325
    *Face palm*
    That makes complete sense. I have no idea why i thought that i could put it outside a job and still expect it to dispose the array for me! XD

    I think i might've tried that once and got an error of some kind (it was probably unrelated now that i think about it... *Sigh*). I'll give that a shot as soon as I'm able.

    To prevent memory leaks when things get destroyed post process? Alright, I'll make sure to throw that line back in.

    Hmm, good to know. And of course, I'll also deallocate the other native containers in the OnDestroyManager as well.

    There probably are quite a few mistakes i made, all of which I'll be looking into right now. I will keep you posted on how things go.

    Thanks for all the help, Attatekjir and 5argon. Wish me luck! (Or wisdom, wisdom is good too).

    Edit:
    I have good news! After applying all of your answers and suggestions everything is now in working order, and not a single error message in sight! Woot! Thanks so much for all the help! (should i update the threads title to say that my question has been answered? Or is that not a thing around here?)
     
    Last edited: Dec 16, 2018
    Attatekjir and Deleted User like this.
  6. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,365
    necroing this.
    So you dispose natives only OUTSIDE an IJob?
    What about temp natives created inside the job?
     
  7. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    If using Allocator.Temp, you don't dispose them. Everything allocated with Allocator.Temp is automatically disposed at the end of job execution.
     
    laurentlavigne likes this.
  8. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,365
    Gotcha, and outside of a job everything needs to be disposed?
     
  9. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    Allocator.Temp has the similar rules outside of jobs except it is auto-disposed per frame and consequently you can't pass it into a job from outside.
     
    laurentlavigne likes this.