Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Unloading prefabs with Resources.UnloadAsset

Discussion in 'Scripting' started by Luke_161, Mar 23, 2016.

  1. Luke_161

    Luke_161

    Joined:
    Mar 3, 2015
    Posts:
    19
    Hello all,

    I'm trying to unload a prefab from memory that's been loaded in with Resources.Load. Example code below:
    Code (CSharp):
    1. public IEnumerator PrefabLoadTest()
    2. {
    3.     Object resource = Resources.Load("PathToPrefab");
    4.  
    5.     GameObject prefab = Instantiate<GameObject>(resource as GameObject);
    6.  
    7.     yield return new WaitForSeconds(1f);
    8.  
    9.     Destroy(prefab);
    10.     prefab = null;
    11.  
    12.     yield return new WaitForSeconds(1f);
    13.  
    14.     Resources.UnloadAsset(resource);
    15.     resource = null;
    16. }
    When I call Resources.UnloadAsset on the loaded Object (not the instantiated one) I receive the following error:
    If I call Resources.UnloadUnusedAssets all the references and memory are cleaned up correctly, which is great. So it seems like Resources.UnloadAsset can't be used for cleaning up prefabs? Is this the expected behaviour or am I missing something? I'd prefer to be able to unload individual prefabs with Resources.UnloadAsset if possible...
     
    ranye likes this.
  2. Hyblademin

    Hyblademin

    Joined:
    Oct 14, 2013
    Posts:
    725
    This is only for clearing up space in memory to load other assets. As such, unloading an asset such as a prefab from memory would effectively make every instance of that prefab in your scene disappear. Since assets are just loaded back into memory when they are accessed again, it basically has no effect in that scenario. However, I have to imagine that Unity remembers where to look in memory to load an asset when it tries to access it, and deleting that memory and reloading it would mess up every subsequent memory read to that asset, since it will likely be in a different location.

    In other words, yes, this is expected.

    What do you need it to do that Object.Destroy() won't do?
     
  3. Luke_161

    Luke_161

    Joined:
    Mar 3, 2015
    Posts:
    19
    A call to Resources.Load for a prefab takes the content off the disk and puts it into memory, including all textures/meshes etc. associated with that prefab correct?

    In the scenario I've outlined I want everything associated with that prefab, and the prefab itself to go back out of memory. The point is to free up memory, to allow other assets to be loaded, once I know I don't need the prefab anymore.

    Object.Destroy() will remove an instance, but it doesn't remove the resource, and if you tried calling Object.Destroy on the resource Unity would (correctly) block you from doing this.

    Resources.UnloadUnusedAssets does remove the prefab and all associated assets from memory correctly, but I'm looking for a way to do it on an Object by Object basis. I'd expect that if I can load something into memory with Resources.Load, Resources.UnloadAsset should let me take it out again when I'm done with it?
     
  4. Hyblademin

    Hyblademin

    Joined:
    Oct 14, 2013
    Posts:
    725
    Yes, but assets are different from objects. Deleting an instance of an object will remove that instance from memory, which frees up a marginal amount of space. On an object by object basis, this is more or less the best you can do.

    Every single instance of the object needs to read from its associated assets in memory in order to exist in the scene. Therefore, if even one instance exists, its asset must also exist in memory. Conversely, in order to unload from memory, there must be no instances present.

    This is the situation as I understand it. This system is why there may be hiccups when instantiating an unloaded asset for the first time, but not subsequent times.
     
  5. Luke_161

    Luke_161

    Joined:
    Mar 3, 2015
    Posts:
    19
    In the example code I've given, there are no more instances of the object, so there's no reason for it to be left in memory.

    To be clear, I see the prefab gameObject and all associated assets in memory, but they don't exist as instances anymore, and they only reference themselves. A call to Resources.UnloadUnusedAssets detects this and removes them, but that's potentially a heavy call.

    Perhaps this just isn't something that Unity currently supports, but I'd of thought that if Resources.Load can identify and load asset dependencies with a prefab resource, Resources.UnloadAsset should support doing the reverse...
     
    NeverTrustShadows and doneykoo like this.
  6. Hyblademin

    Hyblademin

    Joined:
    Oct 14, 2013
    Posts:
    725
    The wording of the error seems to suggest that a prefab is not an "individual asset", which I suppose may be that it's actually several components and textures/meshes/etc and therefore not compatible with the call. Obviously the documentation is unhelpful in this regard... Can you try loading/unloading a texture or something? If that were to work it would support the theory that unloading a prefab isn't supported.
     
  7. Luke_161

    Luke_161

    Joined:
    Mar 3, 2015
    Posts:
    19
    Unloading a texture works fine, I think it just comes down to Unity not considering a prefab resource as an 'asset' that can be unloaded. Would be nice to hear from someone in Unity team about this and whether it's the intended behaviour.

    In case it's useful here's an example project, if you create a build and connect it to the profiler, then click through the various stages you can see how the memory updates (won't work in the editor as it always seems to show everything as loaded). With Resources.UnloadAsset you see the error I mentioned, with UnloadUnusedAssets everything is removed correctly.

    Some grabs from the profiler:

    1) Before the prefabs resource is loaded, notice no Texture2D or GameObjects

    BeforePrefabsLoaded.png

    2) After the prefabs resource is loaded and an instance is created, notice Texture2D and GameObjects come into memory.

    AfterPrefabsLoaded.png

    3) After a call to Resources.UnloadUnusedAssets, notice Texture2D and GameObjects have gone, with UnloadAsset we just get the error message and no memory changes.

    AfterUnloadUnusedAssets.png
     

    Attached Files:

  8. jmcclure

    jmcclure

    Joined:
    May 5, 2009
    Posts:
    48
    I realize that this thread has been dead for a few months, but I want to chime in and say I share your frustration with the lack of memory management in manual resource loading/unloading in Unity. It's something I've struggled with on a couple of different projects now. I too, would love to hear from someone at Unity on this.

    My own research has lead me to believe that locally saved asset bundles have the capability you are looking for (complete and precise memory management per "resource"). I haven't tried it yet myself because of the level of effort involved in keeping a large number of small asset bundles properly up to date. You have to rebuild asset bundles any time any of the assets in the bundles changes, and they have special cross platform considerations.
     
    futurlab_xbox likes this.
  9. Nodrap

    Nodrap

    Joined:
    Nov 4, 2011
    Posts:
    78
    Hmmm, an interesting thread. This seems like a really important thing. If I want to unload a prefab and its textures there seems to be no automatic way of doing that. I'm having to resort to deleting the references and hoping that the UnloadUnusedAssets removes them before I load in replacements that will take up the space that it will free. Feels very easy to get a memory spike if they don't swap out correctly.
     
  10. Binary42

    Binary42

    Joined:
    Aug 15, 2013
    Posts:
    207
    bump x)
     
  11. tiancaiwrk

    tiancaiwrk

    Joined:
    Nov 23, 2017
    Posts:
    35
    Resources.UnloadAsset(resource);
    the "resource" is a GameObject prefab , witch means it's not an "asset",
    if you set unity editor's Asset Serialization to Force Text (menu Edit -> ProjectSetting -> Editor) you can see a GameObject Prefab is a YAML txt info, it is reference other assets like Textures/Meshes but it is not an asset it self.
    that means if you wanna unload assets (QuadTexture / Quad) that referenced by "resource", you must get them from "resource", like this way:
    var t = resource.GetComponent<MeshRenderer>().material.mainTexture;
    Resources.UnloadAsset(t); // if you must use this API

    otherwise you can just destroy "resource" and all Instantiated GameObjects, and call Resources.UnloadUnusedAssets();
    this is more easy and smart way to unload asset but it may cause spike, you must use it wisely
     
  12. DavidSWu

    DavidSWu

    Joined:
    Jun 20, 2016
    Posts:
    183
    Yoiu might want to use resource.GetComponent<MeshRenderer>().sharedMaterial.mainTexture to avoid creating a new material.
     
  13. rhodnius

    rhodnius

    Joined:
    Apr 11, 2012
    Posts:
    1,332

    Seems that Destroying the prefab "resources" then causes an error of "Destroying assets is not permitted to avoid data loss." which now sounds contradictory? :confused:o_O

    I'm trying to do something similar, but haven't found a way to just unload prefabs
     
  14. Jeremy-Lv

    Jeremy-Lv

    Joined:
    Mar 17, 2013
    Posts:
    16
    I also want to find a way to unload the resource from the instance .
    Note that load the object only set a prefab in memory profiler,the resource (texture,animation) are not in memory profiler(? why).

    Only Instance a gameobject form the loaded prefab ,the resrouce (texutre,animation) are show in memory profiler.

    My game has a hero show function, there are 100 more different gameobject need to be instance to the game .
    And there is no way to unload the resource except the Resources.UnloadUnusedAssets(). but this function will cause the game fps to 1 or less than 5.

    Can Unity give me a function to unload the asset what I instance from the gameobject ??
    And why resource.load() only give a object in profiler->memory->prefab, and will not load the (texture ,animation ..) ??
     
  15. Mehrdad995

    Mehrdad995

    Joined:
    Jul 17, 2013
    Posts:
    46
    This is ridiculous, I'm not a knowledged person when it comes to memory management backend in game engines, but the fact that *Every single Prefab or Texture you reference in a script gets Completly loaded into memory* is simply ridiculous.
    Let's say I have a resource holder script as a singleton that holds every resource needed in the game. whenever I access that script Unity loads all the data referenced in the scrip (but not presented in the Scene itself) into memory.
    To clean that up I changed my approach to only hold their path and Load them when needed from the disk, But now I can't unload them. Yes, they can get unloaded on scene transitions by "Resources.UnloadUnusedAssets()" but in my use case which is a car showroom with a lot of cars, I can't just keep adding cars to the memory as the user cycle through them.
    Please help ‍o_O:confused:
     
  16. Nodrap

    Nodrap

    Joined:
    Nov 4, 2011
    Posts:
    78
    I can understand why it is there. Working out what you need to run a particular scene would be really hard if you had to manually tell it what to load. The system for when you want to take control is cumbersome but workable. You can scan through a resource folder to find what is in it to dynamically generate a list of references without loading them in. Have done this with my own sound manager so you can have only the ones you need in at any time.
    One thing I did find recently is the Addressable Asset System, a package from Unity in the Package Manager. Haven't tried it out but it looks to help you reference assets without actually loading them in. Might be just what you're/we're looking for.
     
  17. wwaero

    wwaero

    Joined:
    Feb 18, 2020
    Posts:
    42
    This is kinda driving me nuts as well atm. I can't have all my objects in memory, I have a loader where a user can load one at a time but the objects are too big to have all them in memory. There's gotta be a way to do this.
     
  18. Luke_161

    Luke_161

    Joined:
    Mar 3, 2015
    Posts:
    19
    Coming back to this after 4 years! I ended up switching my workflow towards Asset Bundles and away from Resources. This gives the fine level of control where you can unload an asset (Prefab) and all it's dependencies but it's a lot more overhead to setup vs Resources.

    The new Addressable Assets system makes this a lot easier though and I'd recommend people check that out! I remember seeing an optimisation talk from Unity some time ago which encouraged moving away from using Resources as they add a large overhead to app boot time.
     
    vxdcxw, Mehrdad995 and pep_dj like this.
  19. pep_dj

    pep_dj

    Joined:
    Nov 7, 2014
    Posts:
    178
    +1

    I switched from Resources folder to Addressables and everything is far better now!
     
    vxdcxw likes this.
  20. james_cg

    james_cg

    Joined:
    Nov 13, 2019
    Posts:
    15
    With Addressable (actually assetbundle), we still cannot unload one certain asset, right?

    All we can do, just unload bundle, not one single asset.
     
  21. vxdcxw

    vxdcxw

    Joined:
    Mar 21, 2014
    Posts:
    15
    I made some tests with AssetBundles and Addressables recently and from my tests (looking at the memory profile), unloading an asset with `assetBundle.Unload(true)` will unload the assets of the AssetBundle, and `Addressables.Release(op)` will unload a single asset. Memory is correctly freed as expected
     
    Last edited: Mar 21, 2023