Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity WebGL asset bundle memory is not releasing

I am loading and Caching Asset Bundles using below function in unity webgl:

 IEnumerator DownloadAndCacheAB(string assetName)
    {
        // Wait for the Caching system to be ready
        while (!Caching.ready)
            yield return null;

        // Load the AssetBundle file from Cache if it exists with the same version or download and store it in the cache
        using (WWW www = WWW.LoadFromCacheOrDownload(finalUrl, 1))
        {

            yield return www;

            if (www.error != null)
            {
                Debug.Log("WWW download had an error:" + www.error);
            }

            AssetBundle bundle = www.assetBundle;

            if (assetName == "")
            {
                GameObject abObject = (GameObject)Instantiate(bundle.mainAsset, gameObject.transform.position, gameObject.transform.rotation);
                abObject.transform.parent = this.transform;
                SetTreeShaderSettings(abObject);
            }
            else
            {
                GameObject abObject = (GameObject)Instantiate(bundle.LoadAsset(assetName), gameObject.transform.position, gameObject.transform.rotation);
                abObject.transform.parent = this.transform;

            }

            // Unload the AssetBundles compressed contents to conserve memory
            bundle.Unload(false);

        } // memory is freed from the web stream (www.Dispose() gets called implicitly)
    }

And whenever I want to remove the object i use this function.

 public void RemoveBundleObject()
    {
        //if (www != null)
        //{
        //    www.Dispose();
        //    www = null;
        if (loadBundleRef != null)
        {
            StopCoroutine(loadBundleRef);
        }
        if (this.gameObject.transform.childCount > 0)
        {
            Destroy(this.gameObject.transform.GetChild(0).gameObject);
            System.GC.Collect();
        }
        //}
    }

As you can see I am deleting the first child (which I got from asset bundle) then call GC Collect to force garbage collector. but the problem is that my memory is not releasing whenever i unload/destroy the object. Now you will thinking that how i am measuring the memory? I am using WebGLMemoryStats from here. And i am getting this after several iteration of assetbundle load.

enter image description here

Edit: I am using now WebRequest Class to download assetbundle (find here) but still unable to release the memory and sometime get aw snap error.

Even when I am trying to load an empty webgl build again and again its increase the memory heap and then sometime i get aw snap or memory error and chrome crash.

enter image description here

As you can see the initial load is about 1.2 Mb but when i refresh page and take snap shot, it snapshot brings with incremented memory.

like image 549
Muhammad Faizan Khan Avatar asked Mar 11 '19 13:03

Muhammad Faizan Khan


People also ask

Where does Unity cache asset bundles?

The AssetBundles are cached to Unity's Cache folder in the local storage device. The WebPlayer shared cache allows up to 50 MB of cached AssetBundles.

What is AssetBundle Unity?

An AssetBundle is content that is stored separately from a main game or application and loaded (or downloaded, in the case of mobile and online apps) at runtime. This helps minimize the impact on network and system resources by allowing customers to download and install only the parts they need.


1 Answers

As a note, you'll never get this to function as perfectly as you may think. There are utilities for cleaning up garbage and unloading assets but even if you follow all best practices, you still might not get Unity to clean up all the garbage (it does a lot in the background that'll retain allocated memory and there's not much you can do about it). If you're curious about why this is, I'd suggest their writeup on Heap Fragmentation as well as their Memory Optimization Guide.


As far as what you're doing with your specific project, there are at least some improvements you can make:

1) Avoid using the WWW class at all costs. It creates a lot more garbage than it's worth, doesn't always clean up well, and is obsolete in newest versions of Unity. Use UnityWebRequest to download bundles instead.

2) Don't get in the habit of loading a bundle just to load a single asset and then unloading that bundle. If you have lots of loads occurring at runtime, this will cause thrashing and is a fairly inefficient way of managing your bundles. I noticed you're calling bundle.Unload(false) which means the bundle is being unloaded but the loaded prefab asset isn't. I'd suggest restructuring this in a way that you can:

  1. load the bundle you need
  2. load the assets you need from that bundle
  3. wait for the lifetime of all those assets to end
  4. call bundle.Unload(true)

3) Be careful with your call to StopCoroutine(loadBundleRef); (if loadBundleRef is a Coroutine object that is running your web request and bundle loading logic). Interrupting these async operations could lead to memory issues. You should have something in place that ensures web requests and bundle loads either finish completely or, on failure, throw and let your game recover. Don't allow something like StopCoroutine to interrupt them.

4) System.GC.Collect(); is slow and garbage collection happens periodically anyway. Use it sparingly and you might also want to call Resources.UnloadUnusedAssets before calling it.

like image 125
Foggzie Avatar answered Oct 19 '22 07:10

Foggzie