Search Unity

Feedback Best practices for unmanaged memory management?

Discussion in 'Scripting' started by ZeroG677, May 31, 2023.

  1. ZeroG677

    ZeroG677

    Joined:
    Sep 30, 2016
    Posts:
    27
    Hi!

    I'm currently working on a game that uses its own virtual filesystem, and I load assets by deserializing them at runtime using custom formats, and I need advice on how to go about freeing up these resources when they're no longer used.

    I've learned that objects that inherit from UnityEngine.Object need to be destroyed manually as not to cause a memory leak, and I have a lot of these in the form of Texture2Ds, AudioClips, etc. What I've come up with so far is that these have a wrapper class, that are cached using WeakReferences so the GC takes care of them. Problem is, the GC doesn't run on the main thread, so I can't call Destroy on the unmanaged memory in the finalizer when the GC acts.

    So what I'm doing right now is that I have a ReferenceManager class which keeps track of the amount of references for these assets, so objects that use these add a reference on creation and remove it on destruction, with the unmanaged resources only being freed once the ref count is 0, in conjunction with the previous GC system which can get rid of managed resources no problem.

    I'm wondering if this is good practice? Is it too messy? Is there a better way, maybe a way to let the GC destroy the unmanaged resources?
     
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,104
    A proper approach would be to implement IDisposable interface, imo.
    The examples provided should be more than enough.
     
    Bunny83 likes this.
  3. ZeroG677

    ZeroG677

    Joined:
    Sep 30, 2016
    Posts:
    27
    I did, problem is that Unity doesn't allow freeing up these objects outside of the main thread where the GC runs, as they're Texture2Ds and AudioClips etc.

    And I can't really call Dispose manually after I'm done with them because they might be used by multiple things, hence the ReferenceManager solution, which calls dispose once the reference count is 0.
     
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,104
    Regarding this, you should probably mark them for deletion, not act upon them ad hoc. I'm not sure about weak references.

    I mean everything about this is super dangerous, you want to be really sterile with what you're doing when it comes to UnityEngine.Object, because these objects are beyond your governance.

    You can force GC to do its job when you're ready btw. But if you're tangling UnityEngine.Object and weak references and are hoping for the GC to maintain this, you are perhaps facing a catastrophic leak in the future.

    Why didn't you make a more sophisticated pure C# system that simply refers to Unity's objects, like safe-keeping?
    Inheritance of something that's not really yours is nearly always a bad thing.
     
  5. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,104
    When I say safe-keeping, basically something like an extension of Unity does under the hub. Your objects pretend to be real objects, when in reality they lazily check for the true status of the actual objects they refer to. Nothing but latent proxies, just like game objects are in the C# environment.
     
  6. ZeroG677

    ZeroG677

    Joined:
    Sep 30, 2016
    Posts:
    27
    I believe I'm doing what you're referring to, when I mentioned the wrapper class. These are C# objects that hold references to the unmanaged Unity objects, I mostly work with these and keep references to these in the manager, and call Destroy on the unmanaged object when they're disposed.

    I agree with the ad hoc deletion, I could (and should) act upon them in the main loop.

    As for the GC and weak references, they serve as some sort of cache, so if I'm asking for the same asset multiple times I'll get the same cached one, and it gets removed from the cache once it gets GC'd.

    The project is still early enough that refactoring this is not a huge deal, and that's what I'm looking to do as I also have my doubts about the whole GC method.
     
  7. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,104
    Okay I missed this part.
    So if I understand this correctly, you'd like to automatically release the wrapped object once you dereference its wrapper? Is that the problem? For example you delete one of your objects (that was referring to UnityEngine.Object), but it can't be GC'd because the inner object is still alive, and you can't simply Destroy it because GC doesn't work on the main thread.

    If not, can you provide the use case where you would like things to behave differently?
     
  8. ZeroG677

    ZeroG677

    Joined:
    Sep 30, 2016
    Posts:
    27
    Yup, exactly!

    I do feel a little stupid now however, as I realize I can have a controller GameObject that automatically frees unmanaged resources by going through the cache and freeing up resources from assets that are not longer alive on the main thread.

    This is working good at the moment, and I'm hoping it's not too ugly. It's pretty clean on the programming side as I don't really have to interact with the cache in most of the game's code, and I don't have to manually count references, so I'm hoping this is not a terrible idea.
     
  9. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,104
    Manually counting is a neat idea on paper, but can be also a time bomb. Depends on the rest of your system.

    Yes, and when you want to actively delete something you just mark it for deletion, hit a dirty flag, then sweep from the main thread. Something like that.
     
  10. ZeroG677

    ZeroG677

    Joined:
    Sep 30, 2016
    Posts:
    27
    Works perfect, thanks for the insight!
     
    orionsyndrome likes this.