【问题标题】:Why is this code causing memory leaks?为什么这段代码会导致内存泄漏?
【发布时间】:2017-06-22 16:54:52
【问题描述】:

只有一个默认相机和一个游戏对象。

为什么销毁的游戏对象仍然在内存中?我尝试手动调用 GC、Resources.UnloadUnusedAssets 和其他一些黑客,但没有成功。 我的游戏需要动态创建 GameObjects... ObjectPooling 当然可以完成,但由于玩家将玩几个小时并且有大量不同的精灵,我认为这仍然是一个问题。

public class Game : MonoBehaviour
{

    private List<GameObject> objects;

    // Use this for initialization
    void Start()
    {
        this.objects = new List<GameObject>();
    }

    // Update is called once per frame
    void Update()
    {
        foreach (var item in this.objects)
        {
            Destroy(item);
        }
        this.objects = new List<GameObject>();
        for (var i = 0; i <= 1000; i++)
        {
            GameObject gb = new GameObject();
            gb.name = i.ToString();
            SpriteRenderer renderer = gb.AddComponent<SpriteRenderer>();
            renderer.sprite = Resources.Load("Sprites/grass", typeof(Sprite)) as Sprite;
            gb.transform.position = new Vector3(i, transform.position.y, transform.position.z);
            this.objects.Add(gb);
        }
    }
}

【问题讨论】:

  • 你每帧都在创建和销毁太多的对象,任何游戏都不需要这样
  • @Lestat 我的游戏本身并不会每帧创建那么多对象....这段代码经过精心设计,可以轻松重现问题...如果有人回答为什么内存永远不会,我将不胜感激以如此简单的代码发布(并永远保持增长)......谢谢。
  • 嗨,问题是我的回答中提到的gb.AddComponent&lt;SpriteRenderer&gt;()。您可以注释该行代码以验证该语句。您需要对象池两个示例在我的答案中链接。

标签: c# unity3d


【解决方案1】:

你的代码存在三个问题:

1.问题是gb.AddComponent&lt;SpriteRenderer&gt;();。在场景重新加载之前,添加 SpriteRenderer 似乎不会释放内存。 这是您代码中的主要问题。

2.从Update函数中删除this.objects = new List&lt;GameObject&gt;();。您已经在 Start 函数中完成了此操作。您只会创建更多不需要的列表。

该行代码应替换为this.objects.Clear();,因为这将在下一行添加更多对象之前清除列表。

3。由于性能问题,您应该切勿Update 中调用Resources.Load。这就像每帧加载一个文件,会影响游戏的性能。


ObjectPooling 当然可以,但因为玩家将 玩了几个小时,我认为有很多不同的精灵 仍然是个问题。

没有,也没有。对象池是解决此问题的方法,这样您就不会在每一帧中添加新的SpriteRenderer

阅读this 以获取解释,并阅读ArrayObjectPooling 类,该类可轻松用于对象池。

【讨论】:

    【解决方案2】:

    您的 GameObject 是 COM 对象吗?如果是这样,则通过将引用指针添加到列表中来增加引用指针,但不要通过释放 gb 变量来释放引用计数器。

    【讨论】:

    • 我不确定什么是 COM 对象(我是 C# 初学者),但通过查看 Unity 文档似乎并非如此。谢谢。
    【解决方案3】:

    你有内存泄漏并调用你的垃圾收集器没有做任何事情的原因是你声明的方式

    private List<GameObject> objects;
    

    您在实例化公共类 Game:MonoBehavior 时将其声明为全局变量。

    由于调用了垃圾收集,这最终会将 List 放入第 2 代对象池中,直到不再使用该类时才会被释放。

    我确实看到了两个选项,您可以通过将对象放置在允许普通垃圾收集器释放内存的方法中来缩小对象的范围,因为此时它将成为第 1 代或第 0 代对象。

    或者您可以在列表上调用 TrimExcess(),这会将列表中任何已分配空间的内存减少到该列表中对象实际持有的内存量。

    以下是一些有关 C# 垃圾收集器和对象生成的有用链接:

    https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals

    http://www.dotnettricks.com/learn/netframework/net-garbage-collection-in-depth

    【讨论】:

      猜你喜欢
      • 2011-05-17
      • 2013-10-03
      • 1970-01-01
      • 1970-01-01
      • 2016-04-12
      • 1970-01-01
      相关资源
      最近更新 更多