Unity WebGL 资产包内存未释放

Unity WebGL asset bundle memory is not releasing

我正在加载和缓存 Asset Bundles 统一使用以下函数 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)
    }

每当我想删除对象时,我都会使用这个函数。

 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();
        }
        //}
    }

如您所见,我正在删除第一个子项(我从资产包中获得),然后调用 GC Collect 以强制执行垃圾收集器。但问题是,每当我 unload/destroy 对象时,我的内存都不会释放。现在你会想我是如何测量内存的?我正在使用来自 here 的 WebGLMemoryStats。 我在多次迭代 assetbundle 加载后得到了这个。

编辑: 我现在正在使用 WebRequest Class 下载 assetbundle (find here) 但仍然无法释放内存,有时会出现 aw snap 错误。

即使我试图一次又一次地加载一个空的 webgl 构建,它会增加内存堆,然后有时我会遇到 aw snap 或内存错误和 chrome 崩溃。

如您所见,初始加载约为 1.2 Mb,但当我刷新页面并拍摄快照时,快照带来了增加的内存。

请注意,您永远无法像您想象的那样完美地运行它。有用于清理垃圾和卸载资产的实用程序,但即使您遵循所有最佳实践,您仍然可能无法让 Unity 清理所有垃圾(它在后台做了很多事情,会保留分配的内存,而您却没有多少)可以解决它)。如果您对这是为什么感到好奇,我建议他们在 Heap Fragmentation as well as their Memory Optimization Guide.

上发表文章

就您对特定项目所做的工作而言,您至少可以进行一些改进:

1) 不惜一切代价避免使用 WWW class。它产生的垃圾比它的价值多得多,并不总是能很好地清理,并且在最新版本的 Unity 中已过时。请改用 UnityWebRequest 下载捆绑包。

2) 不要养成加载一个包只是为了加载单个资产然后卸载该包的习惯。如果您在运行时发生大量负载,这将导致抖动并且是一种相当低效的管理包的方式。我注意到您正在调用 bundle.Unload(false),这意味着 bundle 正在卸载,但 loaded 预制资产 没有。我建议以您可以的方式重组它:

  1. 加载您需要的包
  2. 从该捆绑包中加载您需要的资产
  3. 等待所有这些资产的生命周期结束
  4. 致电bundle.Unload(true)

3) 小心调用 StopCoroutine(loadBundleRef);(如果 loadBundleRef 是一个 Coroutine 对象,即 运行 您的 Web 请求和捆绑加载逻辑)。中断这些异步操作可能会导致内存问题。你应该有一些东西来确保网络请求和捆绑包加载完全完成,或者在失败时抛出并让你的游戏恢复。不要让 StopCoroutine 之类的东西打扰他们。

4) System.GC.Collect(); 很慢,而且垃圾回收会定期进行。谨慎使用它,您可能还想在调用它之前先调用 Resources.UnloadUnusedAssets

请也考虑@Foggzie 的回答以获得一些最佳实践(我使用了其中的一些)!

我的代码或测试过程有两个问题。

1。不要使用 WWW.LoadFromCacheOrDownload

Unity 内置 WWW.LoadFromCacheOrDownload 是废话!严重地!为什么 ? Ben Vinson and unity也在他们的博客中解释:

Another source of memory-related problems is the IndexedDB filesystem used by Unity. Any time you cache an asset bundle or use any filesystem-related methods, they are stored in a virtual filesystem backed by IndexedDB.

What you might not realize is that this virtual filesystem is loaded into and persisted in memory as soon as your Unity application starts. This means that if you are using the default Unity caching mechanism for Asset Bundles, you are adding the size of all of those bundles to the memory requirements for your game, even if they are not being loaded.

而 Unity his blog 中明确提到使用 UnityWebRequest 代替 LoadFromCacheOrDownload:

A longer term solution to minimize asset bundle caching memory overhead is to use WWW Constructor instead of LoadFromCacheOrDownload() or use UnityWebRequest.GetAssetBundle() with no hash/version parameter if you are using the new UnityWebRequest API.

Then use an alternative caching mechanism at the XMLHttpRequest-level, that stores the downloaded file directly into indexedDB, bypassing the memory file system. This is exactly what we have developed recently and it is available on the asset store. Feel free to use it in your projects and customize it if you need to.

2。 Chrome 开发工具打开时不测量内存

页面重新加载时的内存增量问题似乎与 FireFox 内部机制有关。但由于在我检查内存时打开了开发工具,它发生在 chrome 中。

一旦我在 chrome 中测量我的选项卡内存,开发工具就会打开,因此它会在每次页面刷新时增加内存。此原因由 Unity official at forum

之一定义

One point to note is that when profiling memory usage on page reloads in Firefox, make sure to have the Firefox web console (and debugger) window closed. Firefox has a behavior that if web console is open, it keeps the Firefox JS debugger alive, which pins all visited pages to be cached in memory, never reclaiming them. Closing the Firefox web page console allows freeing the old pages from memory.

I do not know if Chrome might have similar behavior, but for good measure, it is good to have its web console closed to ensure it is not keeping pages alive.

If someone has a public test case available that they can post, that would be helpful in pinpointing the source.

但实际上 my test suggest 在解决 Google Chrome.

时,这个问题仍然存在于 fireFox 中

记住,我经过长时间的努力得到了这个答案。

更新:我发现当您在 firefox 中重新加载页面时,它会获得双倍的页面内存。所以是firefox的问题,与unity webgl无关。