behaviac icon indicating copy to clipboard operation
behaviac copied to clipboard

关于C#版本Agent实例的内存泄漏问题

Open MysteryAngle opened this issue 4 years ago • 3 comments

Agent类在析构函数的时候调用了以下的代码:

Context.RemoveAgent(this);

Context中持有agent实例的引用,同时这个Context实例又被一个静态字典所引用(Context.ms_contexts),因此析构函数应该永远不会被执行才对。

目前的解决办法是在不需要Agent实例的时候,手动调用以下两个方法:

agent.btunloadall();
Context.RemoveAgent(agent);

另外记得把条件编译符号BEHAVIAC_RELEASE声明一下,因为调试模式下会导致实例被保存到一个静态变量中。

我不知道目前是否还有更好的做法,但是没有更新的情况下只能暂时这样做了,如果有其他的解决办法劳烦告知一下。

MysteryAngle avatar Sep 23 '21 10:09 MysteryAngle

进一步分析,在Workspace这个类里面创建行为树的时候,如下代码:

public BehaviorTreeTask CreateBehaviorTreeTask(string relativePath)

该方法使用了一个成员变量m_allBehaviorTreeTasks来保存所有创建的行为树实例:

// ...
// 创建行为树实例
BehaviorTask task = bt.CreateAndInitTask();
Debug.Check(task is BehaviorTreeTask);
BehaviorTreeTask behaviorTreeTask = task as BehaviorTreeTask;

if (!m_allBehaviorTreeTasks.ContainsKey(relativePath))
{
    m_allBehaviorTreeTasks[relativePath] = new BTItem_t();
}

BTItem_t btItem = m_allBehaviorTreeTasks[relativePath];

//
// 这里实际上永远返回true,因为新的实例肯定在之前是没有保存的
if (!btItem.bts.Contains(behaviorTreeTask))
{
    btItem.bts.Add(behaviorTreeTask); //  这里保存了行为树实例
}

同之前的Context一样,由于Workspace是单例的,因此如果没有从btItem.bts变量里面移除掉的话,实例将不会被释放。 正常来说如果Agent被销毁了,会调用Workspace.DestroyBehaviorTreeTask方法来移除,但是有一个情况例外,那就是如果一个行为树使用了子树,那么这个子树并不会进行释放操作,从而导致了行为树实例不断增长,最终导致内存耗尽。

经过我们的场景分析,将行为树实例保存到Workspace中好像并没有什么用途,因此简单粗暴的解决办法就是,移除掉这些代码,通过内存分析工具观察,可以正常的被释放掉。

MysteryAngle avatar Sep 26 '21 08:09 MysteryAngle

我这里遇到一个相似的问题。 Base/Utils.cs里面有这样一个方法

      private static string ReadToken(string str, int pB, int end)
      {
          string strT = "";
          int p = pB;

          while (p < end)
          {
              strT += str[p++];
          }

          return strT;
      }

看起来跟直接用str.SubString(pB, end - pB) 是一样的效果,不同的是这样处理会多出大量GC

tanghuipang avatar Jan 20 '22 08:01 tanghuipang

我这里遇到一个相似的问题。 Base/Utils.cs里面有这样一个方法

      private static string ReadToken(string str, int pB, int end)
      {
          string strT = "";
          int p = pB;

          while (p < end)
          {
              strT += str[p++];
          }

          return strT;
      }

看起来跟直接用str.SubString(pB, end - pB) 是一样的效果,不同的是这样处理会多出大量GC

C#能写出这种代码不太可能,严重怀疑是cpp写完自动转换的,还有很多while(e.MoveNext)这种写法。

cuixin avatar Jun 10 '22 03:06 cuixin