Search Unity

Release Asset vs Release Instance

Discussion in 'Addressables' started by Allan-Smith, Jul 15, 2018.

  1. Allan-Smith

    Allan-Smith

    Joined:
    Feb 7, 2012
    Posts:
    52
    Hi,

    Sorry if this is answered elsewhere but I couldn't find it in the docs or the forum. Say I load an scriptable object by doing
    var operation = Addressables.LoadAsset<ScriptableObject>(address)
    , then grabbing its reference by
    var reference = operation.Result
    . In this scenario, I imagine that the right way to unload this object is by calling
    Addressables.ReleaseAsset(reference)
    , right?

    However, what if this is a prefab. Say I load it through Addressables, then I go on to populate a pool with 100 instances of it. Then, I leave the scene where the pool is used so I want to clean it up. If I call ReleaseAsset, will this destroy the 100 instances? Do I need to call ReleaseInstance for each of the instances AND THEN ReleaseAsset? Whats the correct way to go about this?

    Best,
    Allan
     
  2. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    158
    You should use Addressables.ReleaseInstance() only for instance which created by Addressables.Instantiate().

    If you populate instances via GameObject.Instantiate(), instances are not destroyed by calling Addressables.ReleaseAsset() because Addressables does not have any references to it.
     
  3. james7132

    james7132

    Joined:
    Mar 6, 2015
    Posts:
    109
    According to the (currently sparse) documentation, you can use Addressables.ReleaseInstance as a replacement for Object.Destroy. If the instance isn't managed by Addressables, it just destroys the object. If it is, it decrements the reference counter for the source asset.

    Though on this topic, I do wish to ask: what happens to instantiated objects destroyed by other means: i.e. scene loads? It would be nice to have an Addressables equivalent to Resources.UnloadUnusedAssets that runs through the list of managed references and cleans up instances that were destroyed by some other mechanism.
     
    Last edited: Aug 7, 2018
    Kichang-Kim likes this.
  4. Allan-Smith

    Allan-Smith

    Joined:
    Feb 7, 2012
    Posts:
    52
    Yes, I understand that. The question is... if I ReleaseAsset on an asset used in 100 instances instantiated with Addressables.Instantiate, are all 100 instances destroyed to free the memory or does it just not work?
     
  5. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    362
    @Allan-Smith -
    Two bits of context before I get to the answer. First, ignoring Addressables and looking a little lower-level. If you load an asset from an asset bundle, instantiate it, then unload the asset and the bundle, you are in an uncertain situation. Some things are fine after being instantiated and some are not. Say your prefab references a texture from that bundle. Unloading it will break the live-link to that texture. So unloading a bundle post-instantiation is does not guarantee disaster, but is unlikely to be a good idea.

    Second bit of context, you have two choices for your instantiating:
    1. If you do Addressables.LoadAsset("x"), then take the result and Instantiate it 100 times, our ref count on "x" will be 1.
    2. If you do Addressables.LoadAsset("x"), then Addressables.Instantiate("x") 100 times, our ref count on "x" will be 101.

    Now, to answer your question. If you do Addressables.ReleaseAsset("x"), you will decrement the ref count by one. In the first scenario, this means the count goes to 0, and the bundle will be unloaded if there's no other still-referenced asset in it. If you are in the second scenario, our ref count will still be at 100, and won't go down until you've called Addressables.ReleaseInstance 100 times.

    Of note, if you put 100 instances into a scene, then close the scene, this is the same as calling ReleaseInstance on those 100 items. We detect the close and react properly.

    So what should you do? Instantiate yourself vs using our API? That's up to you. If you choose to do scenario 1, you need to keep up with your own instance count, and not unload until your instances are gone. If you choose the second scenario, then you could call unload right after instantiating, or potentially never call Load. The only reason to call LoadAsset in the second scenario is if you want to have a loading screen at one point, and not fill in your pool until later.

    Hope this helps,
    Bill
     
  6. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    158
    @unity_bill
    How about @james7132 's question? If I destroy the gameobject from Addressables.Instantiate() by using norma GameObject.Destroy() or new scene loading, what happen? I think reference still exists but actual object is destroyed, so reference leak occur?
     
  7. Allan-Smith

    Allan-Smith

    Joined:
    Feb 7, 2012
    Posts:
    52
    This does help a lot! Thanks for the answer, that was what I was looking for and it makes sense, kind of what I expected would be the case.
     
  8. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    362
    If you do Addressables.Instantiate, then clean up with GameObject.Destroy, we will not know about it, and the ref count will not go down. So that's bad. If you do Addressables.Instantiate, then close the scene that thing is in, we do notice that, and decrement ref counts accordingly. So that's good.

    -Bill
     
  9. james7132

    james7132

    Joined:
    Mar 6, 2015
    Posts:
    109
    Wait so scene unloading does indeed decrement the reference counter (provided we instantiated it with Addressables)? That's not very apparent that is the case from any part of the current documentation.
     
  10. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    362
    I've made a note to add this to our documentation.
     
  11. cruelbob

    cruelbob

    Joined:
    May 26, 2014
    Posts:
    20
    @unity_bill Does this system counts assets referenced from components? Example:
    I have two different windows (2 different prefabs) using same background. Background is texture used in Image component (see uploaded screen).
    Would texture be unloaded if I unload one of windows?
     

    Attached Files:

  12. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    362
    "Yes" to your general question, but "we don't count that texture" would be the more detailed answer....

    - if you have 2 parents, referencing the same other-thing. Load both, that other-thing has a ref-count of 2. Unload one parent, and that other-thing now goes down to one and does not get unloaded.
    - BUT, I want to clarify what exactly we're counting. Your prefab has a direct reference to the texture (more likely a direct reference to a material, which then references the texture). This uses Unity's core loading system, not Addressables. As such, we don't actually ref-count that, it just works. The better example would be if those two prefabs came from the same asset bundle. We'd load and ref-count that bundle, then we'd load the prefabs. The objects that Unity loads into memory are correctly managed internally. If in this example, the prefabs were in one bundle, and the texture another, we'd still count the bundles correctly to ensure nothing gets unloaded out from under you.

    Hope that helps,
    -Bill
     
  13. cruelbob

    cruelbob

    Joined:
    May 26, 2014
    Posts:
    20
    @unity_bill How to find out that some texture used by another prefabs? It turns out that we can't unload any prefabs because this can break other prefabs.
     
  14. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    362
    You don't have to. You load a prefab that requires a texture, Unity loads that texture. You then do that again with another prefab, Unity deals with it. Unload one of the prefabs, and Unity manages everything. If the texture is in an asset bundle, it's not actually unloaded from memory until the bundle is unloaded anyway. Which addressables manages.

    -Bill
     
  15. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    1,270
    @unity_bill I'm curious about how careful I need to be in cleaning up my assets. I generally have a lot functionality that can Destroy() a GameObject in my game. You said that Destroy won't clean up the Addressables, but that loading a new scene will clean things up. So, what happens if I Destroy some game objects, and then later I change scene? Will the bundles I loaded get cleaned up at that point?

    In my game, I completely unload/change scenes fairly frequently. My scene transition approach first loads a "Loading" scene via SceneManager.LoadSceneAsync with allowSceneActivation = true, then once that's showing I additively load the "real" scene. My hope is that LoadSceneAsync will act as a catch-all, unloading anything I've loaded up to that point, so I don't need to worry too much about unloading things. Is that an accurate description of what LoadSceneAsync will do?

    But that raises another question: What if I don't want to unload a particular bundle when changing scenes? Maybe I have many scenes in a row that use the same stuff. Is there a way to keep LoadSceneAsync from unloading things if I want to keep them loaded through a scene transition?

    I tried looking at the profiler, thinking it would clear things up for me, but it's still not quite clear. Here's the profiler showing a bundle I've loaded at the first blue tick mark. At the second blue tick mark, I completely reloaded the scene. This stops any more stuff in the profile, but the entry never goes away. Is it correct that my bundle was unloaded after the second blue tick? Or does its presence in the list imply it's still loaded?
    upload_2019-1-8_21-49-55.png
     
    Last edited: Jan 9, 2019
  16. Rotary-Heart

    Rotary-Heart

    Joined:
    Dec 18, 2012
    Posts:
    440
    @dgoyette If you need a way to keep assets loaded I would suggest you using an object on
    DontDestroyOnLoad
    scene to load your assets, this way they will not be automatically released under any scene change (you need to manually release them). This is currently the way I'm doing it, which is still working as of 0.5.3

    Don't get me wrong I like that Unity can handle the release by itself, but I love that we can decide when to release assets manually using this approach.
     
  17. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    1,270
    Thanks. I've been playing around with this a bit more, and I've determined the following to (hopefully) be true:
    • Objects created via Addressables.Instantiate will be destroyed when changing scenes, decrementing the count, and causing asset bundle to be unloaded automatically.
    • However, if I instead use Addressables.LoadAsset to load a prefab, and make copies of the prefab via GameObject.Instantiate(prefab), changing scenes will NOT cause the asset bundle to be unloaded.
    So, if I always use Addressables.LoadAsset, it doesn't seem like the bundles ever get unloaded when changing scenes, and it's up to me to unload them. If I always use Addressables.Instantiate, then I can likely count on the scene load to clean up any asset bundles.

    So my approach now is for each scene to declare which addresses it uses. When loading the scene, I unload any loaded assets that aren't needed by this scene, and LoadAsset any assets I need for this scene. I pipe everything through a single helper method that performs the Addressables.LoadAsset calls, and never use Addressables.Instantiate.

    This feels like a reasonable approach. It means I can preload any assets the scene needs, and I can keep those assets loaded through the scene transition in the event that the same asset bundle is used by consecutive scenes (which is common), only cleaning them up (unloading) when I transition to a scene that doesn't use that asset.
     
  18. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    362
    yes.

    One additional thing, just for your awareness. You mentioned the potential of calling Destroy on a game object that may have been created by Addressables. One tip I'd suggest is to just replace every GameObject.Destroy call in your game with Addressables.ReleaseInstance(). If you instantiated the thing via addressables, then this will properly clean it up. If you instantiated it via GameObject.Instantiate(), then we'll detect that and just call Destroy for you (not doing any addressables related logic)

    b
     
    MNNoxMortem likes this.
  19. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    1,270
    Thanks for the tip. A couple of quick questions on that:
    • Is there any need/concern around framerate when the cleanup occurs? For example, if I ReleaseInstance on something in the middle of gameplay, could that trigger some long-running process that causes hiccups? I'm just wondering if there's any reason to hold off on ReleaseInstance until the end of a scene.
    • Calling ReleaseInstance instead of Destroy feels pretty ugly to me. I understand the reason for it, but it feels weird calling ReleaseInstance on objects that aren't addressable just in case... Do you know if there's any planned expansion to the package API such that packages (like addressables) can hook into different unity built-in events and do something different/extra? This ReleaseInstance call feels like the sort of thing that should maybe just happen when calling Destroy, if only Unity allowed for that kind of behavior.
     
  20. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    362
    1. framerate - no issues. The main expensive unloading related thing is Resources.UnloadUnusedAssets. We don't call that from Addressables, and would generally recommend you not call it either.
    2. I doubt there will ever be a world where a package could hijack a built in Unity API and modify it's behavior. The engine could add OnWhatever callbacks, but not a wholesale injection into an interface.
     
    MNNoxMortem likes this.
  21. jonagill_ag

    jonagill_ag

    Joined:
    Jun 21, 2017
    Posts:
    11
    @unity_bill I'm looking into Addressables as a way to get our app's RAM usage down (right now we have a lot of direct object references, so most of the content in the game is loaded at startup). I'm trying to work out what the lifecycle is of an asset loaded through Addressables in order to make sure we set up our systems to use it properly.

    When does this bundle unload happen? Is it immediate, or is there some LRU cache that keeps frequently referenced bundles in memory for a while? The docs mention configuring CacheInitializationSettings, but I can't work out how they're supposed to be used.

    If we release an asset that does still have references to a loaded asset inside of it, when does the bundle get unloaded? For example, let's say we called Addressables.Instantiate() to load a prefab containing a material referencing a texture from a bundle. This loads the bundle, then loads the prefab, material, and texture, and instantiates an instance of the prefab. If we call Addressables.ReleaseInstance(instance) immediately, I'd expect it to destroy the instance and unload the prefab, material, and texture. (Please correct me if I'm wrong here).

    However, let's say I made a copy of the material before unloading. Now we have a copy of the material in memory, which references the texture. This means that the bundle cannot -- and may never -- be unloaded, right?


    I think what I'm craving is something equivalent to AssetBundle.Unload(true), which to my understanding unloads every asset in the bundle, regardless of whether any references currently exist to those assets. If there are no Addressable references to assets in my bundle, I want the system to forcibly unload it, even if some system somewhere is holding onto a sloppy C# reference somewhere. Is this possible with the current system?
     
  22. jonagill_ag

    jonagill_ag

    Joined:
    Jun 21, 2017
    Posts:
    11
    Digging into it more today, it looks like my testing is being confounded by this issue: https://forum.unity.com/threads/textures-are-referenced-after-asset-release.569860/

    Testing on the latest version of Addressables, I'm still seeing textures hang around in the Memory Profiler even after I release all references to the addressable. Hopefully this is fixed in v1.0!

    That said, I imagine that won't fix still won't be equivalent to AssetBundle.Unload(true), since it sounds like Addressables will intentionally keep assets from the bundle loaded until all references to them have been nulled out. Some way to configure the ResourceManager to aggressively unload assets when unloading bundles would be extremely helpful. Otherwise, we're probably going to have to stick with manually loading and unloading AssetBundles on this project.

    Update: I started poking around in the source code, and I can see that the ResourceManager project's AssetBundleProvider does call AssetBundle.Unload(true), which is great! However, since I'm not seeing the bundle actually unload in my tests, I'm guessing that there is some kind of caching happening to prevent the bundle from unloading as soon as all its references are cleared.

    I can see that there is a CachedProvider set up to wrap AssetBundleProvider in that package, but it's in a Samples directory, and I can't see any equivalent code actually initializing the various providers in the Addressables package. If anyone knows where Addressables is doing this initialization, I'd love to know so I can configure the CachedProviders correctly for our project.
     
    Last edited: Jan 29, 2019
  23. unity_bill

    unity_bill

    Unity Technologies

    Joined:
    Apr 11, 2017
    Posts:
    362
    quick note, I'm making the comments below based on our working branch. I think most apply to 0.5.3, but some may be still-to-come

    on each group, there are settings for the Bundle Cache Provider Max LRU Count & Age Use that to control if there is an LRU holding on to bundles in memory or not.

    this is for the bundle cache used to hold bundles on-device after download. Has nothing to do with in-memory.

    it decrements them all. If they are all 0, they will be ready to unload. If there is nothing else in that bundle with a ref count greater than 0, the bundle(s) will be unloaded.

    Say you do roughly this (not real code!):
    var op = LoadAsset("myMaterial")
    var obj = op.Result
    MakeACopy(obj)
    ReleaseAsset(obj)

    after that, things will probably go boom. You've told us you're done with the material, so we've decremented both the material and texture. The texture will be pulled form memory, and your copied material will be referencing a ghost.

    Similar situation:
    var op = LoadAsset("somePrefab")
    GameObject.Instantiate(op.Result)
    ReleaseAsset(op.Result)

    If that prefab referenced a texture, we've unloaded it, so the instantiated prefab will be textureless.

    we always use (true). So when our refcount is 0, that thing gets unloaded. Forcibly.

    yup, that's a thing. should be fixed in the next release.
     
  24. jonagill_ag

    jonagill_ag

    Joined:
    Jun 21, 2017
    Posts:
    11
    Ah, I didn't realize that the LRU cache was configured per-group! I'll test that out tomorrow.

    Thanks for the comprehensive response, @unity_bill!

    I'm extremely impressed with what I've seen of Addressables so far, and I can't wait to dig into more of it.
     
    unity_bill likes this.