【问题标题】:Unity Board Game AI Causing Memory LeakUnity 棋盘游戏 AI 导致内存泄漏
【发布时间】:2018-02-04 10:10:57
【问题描述】:

我正在创建一个类似于 tic tac toe 的棋盘游戏,并且我已经创建了一个 AI 来玩该游戏。 AI 是 CPU 密集型的,所以我决定把它放在它自己的线程上。我正在使用这个插件来做多线程:https://www.assetstore.unity3d.com/en/#!/content/15717

我有这个 IEnumerator:

static IEnumerator executeAITurn(Turn turn) {
    Vector2[] move = mctsManager.mcts(new State(sections, null, turn), AIIterations)[0, 0].metaData.lastMove;

    yield return Ninja.JumpToUnity;

    input(move, true);

    yield return Ninja.JumpBack;

    Debug.Log("DONE!");
}

我使用它运行它

gameManager.StartCoroutineAsync(executeAITurn((AITurn == Turn.X) ? Turn.O : Turn.X));

通常,当我运行 executeAITurn 时,它可以正常工作,但由于某种原因,有时当我运行它时,它会执行应有的操作,但在任务管理器中,我的内存刚刚开始以 30 mb / 秒的速度增加。内存一直增加到 1000 mb,游戏变得非常慢。当我关闭播放模式时,内存有时会继续增加或停止在原来的位置。我必须通过任务管理器结束 Unity 以释放内存。

我尝试过的一件事是用常规 for 循环替换 foreach 循环,这似乎有所帮助。内存增加的速度下降了很多(最初它会以大约 100 mb / sec 的速度增加)。

任何帮助将不胜感激。

下面是executeAITurn涉及的部分代码:

mctsManager 类:https://pastebin.com/yzeHrY2p

输入函数:https://pastebin.com/8f2hzZws

【问题讨论】:

  • 你试过调试它吗? Unity 有一个内置的分析器。
  • 我刚刚检查了分析器,大部分内存似乎来自 ManagedHeap.UsedSize (424.9 MB)、System.ExecutableAndDlls (301.0 MB) 和 ManagedHeap.ReservedUnusedSize (163.1 MB)。我不确定我应该如何处理这些信息。 reservedUnusedSize 应该这么高吗?
  • 有没有尝试联系多线程插件的作者?
  • @DavidOliver 我没有。我现在试试
  • 您的 generateMoves 函数正在递归调用自身。您可能想要跟踪您在递归中的深度,并在遇到一些您不期望的大数字时退出控制台。

标签: c# multithreading unity3d memory-leaks


【解决方案1】:

这个问题的所有答案确实帮助我更好地管理内存。我只是想我会在一个问题中总结每个问题的重要内容。

首先,在罗伯特利文斯顿的回答中,他提到了这篇文章:https://unity3d.com/learn/tutorials/topics/performance-optimization/optimizing-garbage-collection-unity-games,它实际上涵盖了对我有帮助的所有内容。我希望我事先阅读过。

无论如何,对我帮助最大的三件事是避免装箱、清除列表和缓存(文章中提到了所有这三件事)。为了避免装箱,我还研究了值类型和引用类型之间的区别,这导致我将我的两个类更改为结构,这有助于记忆。

对记忆的最大帮助可能是清除列表,因为它们被非常频繁地创建,但我没有清除它们导致大量垃圾堆积。 (这就是为什么我把赏金给提到清除列表的人)。

不管怎样,谢谢大家的帮助。

【讨论】:

    【解决方案2】:

    这听起来更像是垃圾回收问题。我查看了您的 MCTS 代码并注意到以下内容。

        // Use this for initialization
        void Start () {
    
        }
    
        // Update is called once per frame
        void Update () {
    

    这是您的第一个代码 C# 输入函数中缺少的。 基本上,这是调用更新函数。正如您可能注意到的,它说更新函数每帧调用一次。参考这个https://unity3d.com/learn/tutorials/topics/performance-optimization/optimizing-garbage-collection-unity-games

    我会推荐一个缓存。

    希望对您有所帮助。

    【讨论】:

    • 为什么我的输入函数中有一个开始和更新函数?
    • 那篇文章真的很好。感谢那。我肯定会尝试实现更多的缓存,并且像其他人提到的那样清除列表也非常有用。
    • 好吧,void Update() 和 LateUpdate() 基本上每帧都调用代码。这可能会增加很多不需要的垃圾。例如,如果您有一个甚至没有被使用或单击的对象,则没有理由让该代码在每一帧都触发。垃圾会很快堆积起来,带来沉重而缓慢的体验。很高兴听到这有帮助,祝你好运!
    【解决方案3】:

    在处理内存泄漏时,您必须非常小心,因为应用程序中的每一步都很重要。

    对于初学者来说,每个循环都可能导致内存增加。 尝试从使用循环反转到使用字典和哈希表,因为它正在比较哈希码,所以它会花费你的“无序”数据,但我认为你不需要为你的数据指定一个特定的顺序游戏。

    说明: 您拥有的每个数组列表,如果您知道数组的确切大小,请使用 ArrayList。如果您使用列表,则将其切换到字典(如果您知道列表的类型)或哈希表(如果不知道)。这样你就可以通过key-value的方式获取信息,速度更快,性能更好。

    其次,在实例化新对象时尝试使用 Using 语法,这将帮助您实现 iDisposable 接口以更有效地处理对象。

    第三,尽可能防止代码装箱变量! 装箱意味着,将值类型存储在堆中而不是堆栈中的过程,因此在堆栈中您只有对变量位置的引用。

    第四,使用工具监控代码并检查应用程序中可能存在的内存泄漏。 除了分析器之外,还有很多工具,所以只需搜索它,您就会找到可以帮助您的东西。

    【讨论】:

    • 我现在很忙,但稍后我会尝试进行这些更改并告诉您是否有帮助。您还可以给我一个示例,说明如何使用字典/哈希表代替循环。我看不出它们之间的关系。
    • 我知道我已经问过你这个问题了,但你能否详细说明一下你所说的用字典替换循环的内容。我根据第三个技巧做了很多更改,这非常有用。内存泄漏不再那么严重了,但它仍然很明显,所以我想尽我所能让它变得更好。
    • 这根本不回答问题,它只提供应该作为评论给出的一般建议。最重要的是,有些建议是完全错误的。
    • 我想是因为问这个问题的人说它确实有帮助。
    • 没有真正考虑this跟进问题。
    【解决方案4】:

    第一个任务管理器不是衡量甚至memory demand 性能的有效工具。这些数字可以是高也可以是低。可能在同一时间。

    其次,Garbage Collector 的本质使得测量实际使用了多少内存非常棘手。 GC 将尝试尽可能少地运行。如果它只在应用程序关闭时运行,那是理想的情况。

    如果您排除了这两种常见误解中的任何一种,通常托管运行时中的内存泄漏意味着一件事: 您将某些内容添加到集合(数组、List、Dictionary)但忘记再次取出它。这是发生内存泄漏的唯一方法。尤其是 GC,所以我们不会再遇到“我忘记释放内存”的问题了。

    更罕见的情况是Disposing 出现错误。如果您直接处理非托管资源,您应该首先编写终结器,然后是 Dispose。 如果您处理任何实现 IDisposeable 的操作,即使您的所有 Dispose() 所做的只是将订单中继到包含的实例,也始终实现 IDisposeable。永远不要在该实例和 GC 之间传递 Finalize 命令。

    【讨论】:

    • 首先,作为对您的第一个声明的回应,我还统一运行了分析器,在 ai 运行了大约 1.5 分钟后,我分配的总内存为 2.2 GB,几乎所有内存都即将到来来自 ManagedHeap.Used 大小 (1.4 GB)
    • 其次,如果您查看我的 mctsManager 脚本中的第 85-113 行,就会发现有一个函数涉及 Section 数组列表。另请查看我使用该功能的第 205 行。我不会在任何地方摆脱列表中的东西。你认为这是问题所在吗?
    • 这肯定是内存泄漏的原因。 GC 可以确定对象是否未使用的一种方法是不再存在对它的(强)引用。如果至少有一个,它就不能再收集它了,那么它可能是你当前函数中的临时变量。
    • 完成后我开始清理列表,这对我很有帮助,谢谢!
    猜你喜欢
    • 2013-01-15
    • 1970-01-01
    • 1970-01-01
    • 2016-03-09
    • 2021-11-12
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多