Search Unity

Bug Resources.UnloadUnusedAssets cannot unload assets without any references.

Discussion in 'Addressables' started by zhuxianzhi, Apr 6, 2021.

  1. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    Code (CSharp):
    1.  private void Update()
    2.     {
    3.         if (Input.GetKeyDown(KeyCode.K))
    4.         {
    5.             a = Addressables.LoadAssetAsync<TextAsset>("masterMemoryDB1");
    6.             b = Addressables.LoadAssetAsync<TextAsset>("masterMemoryDB2");
    7.         }
    8.  
    9.         if (Input.GetKeyDown(KeyCode.R))
    10.         {
    11.             Addressables.Release(a);
    12.             Debug.Log("Release A");
    13.         }
    14.  
    15.         if (Input.GetKeyDown(KeyCode.A))
    16.         {
    17.             Addressables.Release(a.Result);
    18.             Addressables.Release(b.Result);
    19.             Debug.Log("Release A,B");
    20.         }
    21.  
    22.         if (Input.GetKeyDown(KeyCode.U))
    23.         {
    24.             _asyncOperation = Resources.UnloadUnusedAssets();
    25.             _asyncOperation.completed += operation => { Debug.Log("UnloadUnusedAssets complete"); };
    26.         }
    27.     }
    When I press K key to load asset A and B, press R key to unload asset a, and the profiler shows that asset a has no references. Call Resources.UnloadUnusedAssets() again, asset A cannot be unloaded.

    If I uninstall A and B, the asset can be uninstalled normally.

    https://docs.unity3d.com/Packages/c.../MemoryManagement.html#when-is-memory-cleared


    QQ截图20210406111724.png


    addressables 1.16.16
    Unity 2020.3.0f1
     
    Last edited: Apr 6, 2021
  2. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,460
    If A and B are in the same bundle, that is expected. We're working on getting better connection tracking between AssetBundles and the Assets they loaded in into the memory Profiler backend. As you noticed, this connection is currently not reported to the Profiler, making it seem as though there would be no reference to it.
     
  3. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,460
    It's also described in the Documentation page you linked to:

     
  4. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    Thanks Reply.

    AssetBundle can achieve more detailed memory management. Why doesn't Addressables.Release call Object.Destroy?
    I can only build A and B into two bundles? But this way will cause Serialized File Overhead.

    Code (CSharp):
    1.            
    2.            var ab = AssetBundle.LoadFromFile("Ab");
    3.            var a = ab.LoadAsset<TextAsset>("a");
    4.            var b = ab.LoadAsset<TextAsset>("b");
    5.            Object.Destroy(a);
     
  5. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,460
    I believe it does. The object is gone. If you had multiple instances of it, they'd be all gone but the native memory associated with the asset is still there because it was loaded into memory as part of the bundle.

    Yes, to a degree. Depending on how you load them (i.e. single assets from file or the entire bundle directly into memory) you can control when what gets loaded in, just not that it gets unloaded before the bundle gets unloaded.

    If you need this fine grained control on unloading, you need to split the bundles. The serialized file overhead is something that needs to be part of weighing that decision over.
     
    zhuxianzhi likes this.
  6. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122

    I read the document carefully

    "You can load an AssetBundle, or its partial contents, but you cannot partially unload an AssetBundle. No Asset in 'stuff' will unload until the bundle itself is completely unloaded. The exception to this rule is the engine interface Resources.UnloadUnusedAssets. Executing this method in the above scenario will cause 'tree' to unload."

    According to my test case, after loading A and B at the same time, only Release A, calling Resources.UnloadUnusedAssets should be able to unload A.

    What did I miss?
     
  7. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    Can anyone answer my question?

    Thanks in advance
     
    Elringus, mkowpak and VoodooDetective like this.
  8. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    I use version 1.16.19 and use Resources.UnloadUnusedAssets in the above situation and it works normally.

    Newer versions such as 1.18x will not work properly.

    @MartinTilo @davidla_unity
     
  9. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,460
    Hey again,
    according to the devs on addressables, nothing affecting this should have changed but it might be a bug. Could you please file a report through Help -> Report a Bug in the Editor?
     
  10. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    Case 1344093
     
    MartinTilo likes this.
  11. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    Last edited: Jul 12, 2021
  12. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,460
    zhuxianzhi likes this.
  13. KospY

    KospY

    Joined:
    May 12, 2014
    Posts:
    153
    Any news on this? It's marked "Fix In Review for 1.18.14", but latest version of addressable is 1.19.9.
    We found this issue recently and it's terrible (using 1.19.6), game memory overload after some loading and nothing can be cleared from memory, does there is any workaround?

    Edit: After more testing it seem that nothing can be released until all assets have ref count to 0 on a bundle. So what is described in the doc, about Resources.UnloadUnusedAssets is wrong, it don't force asset to unload, you really have to have zero asset used from a bundle to be able to free memory. That seem mandatory, you better have to think about your bundle organization to be able to unload everything to free memory. At least until UnloadUnusedAssets is fixed.
     
    Last edited: Oct 15, 2021
    Elringus and Procyonx_ like this.
  14. ysalpha

    ysalpha

    Joined:
    Feb 2, 2017
    Posts:
    52
    If this problem is true, then Addressables is a system with a major problem: memory is not unloaded.
    I still haven't heard anything back from them, does that mean they are still investigating?

    I found this document, https://docs.unity3d.com/Packages/com.unity.addressables@1.19/manual/MemoryManagement.html
    In the first place, Why do we need to call Resources.UnloadUnusedAssets() when we are using Addressables?
    Isn't one of the features of Addressables that it can manage memory well as long as the handle is properly Release()?
    I thought that moving to Addressables would have the advantage of eliminating the need to call the unusually burdensome Resources.UnloadUnusedAssets(). But reality seems to be different.
    Even though 'Resources' is officially deprecated, Addressables doesn't look much different In memory management.
     
  15. RINNUXEI

    RINNUXEI

    Joined:
    Sep 3, 2015
    Posts:
    32
    As with Addressables 1.20.3, Resources.UnloadUnusedAssets still cannot free memory of assets in a bundle when any asset in that bundle has a ref count greater than 0. The document seems quite misleading.
     
  16. RINNUXEI

    RINNUXEI

    Joined:
    Sep 3, 2015
    Posts:
    32
    Reported a bug,
    CASE IN-11667
     
  17. pillakirsten

    pillakirsten

    Unity Technologies

    Joined:
    May 22, 2019
    Posts:
    346
    Hi all I can see that we will need to clarify that example in the docs. But the the key point is "If you release tree, it's ref-count becomes zero, and the blue bar goes away". Then the following rules apply: "In this example, the tree asset is not actually unloaded at this point. You can load an AssetBundle, or its partial contents, but you cannot partially unload an AssetBundle..."
    https://docs.unity3d.com/Packages/c...ent.html#understanding-when-memory-is-cleared

    AssetBundles (and the assets they contain) are fully unloaded if they have no assets in use (aka the bundle has a ref count of 0). This means that assets "released" through Addressables aren't unloaded until their bundle can be unloaded.

    You can use the Resources.UnloadUnusedAssets to unload these "released" objects, although it's not recommended. It is a slow operation that can cause frame hitches. This is because it traverses the entire dependency tree to determine if an asset is unused. Additionally if the unloaded asset is loaded again from the same bundle that hasn't been reloaded, loading the asset fail.
     
  18. RINNUXEI

    RINNUXEI

    Joined:
    Sep 3, 2015
    Posts:
    32
    Hey, thank for your reply.

    But this is still not true as I wrote in the bug report. You can not truly unload a "released" object by using Resources.UnloadUnusedAssets until its asset bundle itself is also being released, that is, all assets in the bundle has 0 ref-count.
     
  19. pillakirsten

    pillakirsten

    Unity Technologies

    Joined:
    May 22, 2019
    Posts:
    346
    I see @RINNUXEI, we will investigate the ticket. Thank you!
     
    MartinTilo and RINNUXEI like this.
  20. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    @pillakirsten @MartinTilo Hey, Any updates on this? Addressables is not working as documented (Resources.UnloadUnusedAssets not unloading released assets) for more than 3 years now.

    Also "if the unloaded asset is loaded again from the same bundle that hasn't been reloaded, loading the asset fail" — sounds like a bug (though we can't verify this because the released assets are not unloaded in the first place).

    Unloading released assets from bundle is an essential feature; you can't really expect each asset to be perfectly distributed between bundles. It doesn't matter if "Resources.UnloadUnusedAssets" is slow, as this kind of operation is intended for loading phases.

    Currently the only workarounds are either using "PackSeparately" which imposes an unacceptable overhead or manually sorting assets between bundles which is time consuming and is not a solution anyway, since not all assets can be split like that.
     
    Last edited: May 4, 2023
  21. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
  22. AlkisFortuneFish

    AlkisFortuneFish

    Joined:
    Apr 26, 2013
    Posts:
    973
    Very much so, and it's one that we've been affected by in multiple cases. It means that if you use a complex dependency tree, which we do, UnloadUnusedAssets is an operation that must *never* be performed, which isn't great in terms of memory usage. Also, when that happens the failure is insidious, instead of an actual error, a bare UnityEngine.Object is loaded, but backed with the correct type of managed wrapper, so you get really weird and broken stuff.
     
    Elringus likes this.
  23. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    In the bug report I've asked to make sure assets can be loaded back after they'll (hopefully) fix the unload issue. I would have created another report specifically for that, but not sure how to reproduce it when we can't unload in the first place.

    To be honest, I'm shocked that such an essential use case (granular asset unloading) is still not covered by addressables. I believe that's a must have for production-ready asset management system. Without it users have to either live with terribly inefficient memory usage (the default "Pack Together" mode) or go through painful process of manually labeling the assets to sort them between bundles; and even in the latter case, it's not possible to perfectly separate all the assets, and memory usage will still be inefficient. Where is Unity's "performance by default" motto here?
     
  24. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    Do they even read the reports?..


     
    NGC6543 and AlkisFortuneFish like this.
  25. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
  26. AlkisFortuneFish

    AlkisFortuneFish

    Joined:
    Apr 26, 2013
    Posts:
    973
  27. pillakirsten

    pillakirsten

    Unity Technologies

    Joined:
    May 22, 2019
    Posts:
    346
    Hi all in regards to the original issue (IN-11667) there was an issue with the reproduction project so it was closed. There was an asset still being referenced and so Resources.UnloadUnusedAssets could not unload the asset.

    Thank you for your patience and submitting a new issue. We hope to investigate it as soon as possible.
     
  28. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    So I got the issue closed as "Won't Fix" with the following response:


    It doesn't make any sense. The only officially documented way to unload assets loaded via Addressable API w/o unloading the whole bundle is to use Resources.UnloadUnusedAssets. At the same time, it's not working and won't be fixed and they don't recommend using it...
     
    zhuxianzhi likes this.
  29. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    filed a new bug report CASE IN-47541.


    Capsule.prefab depends on a texture from textures_assets_all.bundle.

    After releasing the Capsule.prefab, call Resources.UnloadUnusedAssets() to check whether the dependent textures can be released. Addressables can't, but AssetBundle API can.

    PS: The .bundle file is directly built using Addressables
    Code (CSharp):
    1.     private AsyncOperationHandle<GameObject> AsyncOperationHandle;
    2.     private AssetBundle prefabAB;
    3.     private GameObject pGo;
    4.     public bool IsUseAssetBundle;
    5.  
    6.      void Start()
    7.     {
    8.         if (IsUseAssetBundle)
    9.         {
    10.            var shaderPath = Directory.GetFiles(Application.streamingAssetsPath +
    11.                                @"\aa\StandaloneWindows64","*.bundle").Where((s => s.Contains("myShader_"))).ToArray()[0];
    12.             AssetBundle.LoadFromFile(shaderPath);
    13.  
    14.             var texturesAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath +
    15.                                                       @"\aa\StandaloneWindows64\textures_assets_all.bundle");
    16.  
    17.             prefabAB = AssetBundle.LoadFromFile(Application.streamingAssetsPath +
    18.                                                 @"\aa\StandaloneWindows64\prefab_assets_all.bundle");
    19.  
    20.  
    21.             texturesAB.LoadAsset<Sprite>("Assets/Textures/hypergryph.png");
    22.             var go = prefabAB.LoadAsset<GameObject>("Assets/Prefab/Capsule.prefab");
    23.             pGo = GameObject.Instantiate(go);
    24.         }
    25.         else
    26.         {
    27.             Addressables.LoadAssetAsync<Sprite>("Assets/Textures/hypergryph.png");
    28.             AsyncOperationHandle = Addressables.InstantiateAsync("Assets/Prefab/Capsule.prefab");
    29.         }
    30.     }
    31.  
    32.     void Update()
    33.     {
    34.         if (Input.GetKeyDown(KeyCode.R))
    35.         {
    36.             if (IsUseAssetBundle)
    37.             {
    38.                 GameObject.Destroy(pGo);
    39.                 prefabAB.Unload(true);
    40.             }
    41.             else
    42.             {
    43.                 Addressables.Release(AsyncOperationHandle);
    44.             }
    45.  
    46.             Debug.Log("Release GameObject");
    47.         }
    48.         else if (Input.GetKeyDown(KeyCode.U))
    49.         {
    50.             Resources.UnloadUnusedAssets();
    51.             Debug.Log("UnloadUnusedAssets");
    52.         }
    53.     }
     
    Last edited: Jul 13, 2023
    Elringus likes this.
  30. zhuxianzhi

    zhuxianzhi

    Joined:
    Mar 30, 2015
    Posts:
    122
    I implemented a custom loading logic based on Addressables, and then I found that Resources.UnloadUnusedAssets() can’t unload unused assets. After in-depth investigation, I found that Addressables and my custom implementation of the loading logic call Resources multiple times Unused assets can also be unloaded after .UnloadUnusedAssets().

    After further investigation, it was found that it was caused by AssetBundle.LoadAssetAsync, if using
    AssetBundle.LoadAsset only needs to call Resources.UnloadUnusedAssets() once to unload unused assets.

    I tried to use 2 versions of Unity and both have the same problem
    2021.3.4f1 , 2022.1.5f1
    Addressables version:1.21.14

    @pillakirsten

    Code (CSharp):
    1.     async void Start()
    2.     {
    3.         if (IsUseAssetBundle)
    4.         {
    5.             await Addressables.InitializeAsync().Task;
    6.  
    7.             IResourceLocation resourceLocation = Addressables.GetIResourceLocation<Sprite>("Assets/Textures/Grids.jpg");
    8.  
    9.             for (int i = 0; i < resourceLocation.Dependencies.Count; i++)
    10.             {
    11.                 var ab = AssetBundle.LoadFromFile(resourceLocation.Dependencies[i].InternalId);
    12.                 if (i == 0)
    13.                 {
    14.                     texturesAB = ab;
    15.                 }
    16.             }
    17.  
    18.             SpriteRenderer.sprite = texturesAB.LoadAsset<Sprite>("Assets/Textures/hypergryph.png");
    19.             texturesAB.LoadAsset<Sprite>("Assets/Textures/Grids.jpg");
    20.         }
    21.         else
    22.         {
    23.             await Addressables.InitializeAsync().Task;
    24.             var operationHandle = await Addressables.LoadAssetAsync<Sprite>("Assets/Textures/hypergryph.png").Task;
    25.             SpriteRenderer.sprite = operationHandle;
    26.             await Addressables.LoadAssetAsync<Sprite>("Assets/Textures/Grids.jpg").Task;
    27.         }
    28.         await Task.Delay(TimeSpan.FromSeconds(1));
    29.         Resources.UnloadUnusedAssets();
    30.         //Unloading unused asset "Assets/Textures/Grids.jpg" expected
    31.     }
     
    Last edited: Jul 26, 2023
    brian_imvizar likes this.
  31. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    Last edited: Aug 30, 2023
    TeorikDeli likes this.
  32. XxPleYxX

    XxPleYxX

    Joined:
    Jul 26, 2011
    Posts:
    41
    Any news with this? I think it is important enough to give it maximum priority
     
  33. Elringus

    Elringus

    Joined:
    Oct 3, 2012
    Posts:
    483
    So they've closed the issue again as "By Design". In the resolution note they've just admitted docs were wrong and they'll fix them. Basically, unloading individual assets from bundle is not possible. The only workaround they suggested is manually distributing assets between bundles, which won't solve the issue completely (memory usage will still not be optimal) and is just unrealistic even with medium-sized projects, at it involves lots of manual labor and is error-prone.

    I've submitted a suggestion to fix this via https://unity.com/roadmap/unity-platform/pipeline-integrations, but it's unlikely to get any attention, unless major entity/partner raise this.
     
    crowmaster likes this.