【问题标题】:Recursion leading to out of memory递归导致内存不足
【发布时间】:2009-03-16 07:07:46
【问题描述】:

我有 2 GB 的 RAM。我们有一个执行导出/导入操作的应用程序。 我们有一个递归函数,它有一个 Set 类型的局部变量,每次迭代都会不断填充。这个 Set 不断增长,有时我们会耗尽内存。

是否有任何替代数据结构可以优化使用内存?

这是粗略的代码

GetObjectsForExportImpl(long lExportOptions, __int64 numIdProject, XExportSets
     &exportSets, long lClientId, CComPtr<IEPIPDServer> ptrIPDServer,FILE *fp)
{
    XExportSets exportLocal;   //Thats a structure containing the Set
    QueryObjectsForExport(lExportOptions, numIdProject, exportLocal,
         lClientId, ptrIPDServer);
    SetIDs::iterator it = exportLocal.setShared.begin();

    for (; it != exportLocal.setShared.end(); ++it)
    {
         //recursive call
         pExportObject->GetObjectsForExportImpl(lExportOptions,
             numIdProject, exportSets, lClientId, ptrIPDServer,fp);
    }
}

【问题讨论】:

  • 您能更具体地介绍一下您的算法吗?粗略描述一下你对集合做了什么以及算法如何递归。

标签: memory recursion


【解决方案1】:

另一种结构不会有太大帮助。假设您切换到使用一半内存的类。不过,你只是在拖延厄运。

2GB 的结构大小通常表示您需要切换到基于磁盘的结构、数据库或内存映射哈希表。

【讨论】:

  • 是的,我已经实现了基于磁盘的算法。它确实帮助了我一点。但在某些时候内存确实会耗尽
  • 一开始没有数据,内存怎么会耗尽?
  • 很抱歉让您感到困惑。我需要一些数据供内存参考
  • 所有内容都可以存储在磁盘上,包括您的参考资料。
【解决方案2】:

逐个处理数据,而不是一次性处理所有数据。

即:

while (not end-of-file) {
   data = read_some_information();
   export_some_information(data);
}

除非您在非常特殊的情况下需要所有数据才能进行导出(这极不可能)

【讨论】:

  • 这就是问题所在。 Set 中的元素是相互依赖的,所以我无法清除它们。
  • 依赖关系是独立的还是真的任何元素都可能依赖于任何其他元素?
  • 真正重要的是:您需要先前元素的所有数据还是需要某种累积值(如总和或平均值)。如果您确实需要从以前的每个集合中查询数据,那么无论如何,用它们填充数据库,然后查询它以创建导出。
【解决方案3】:

一会儿,比较一下你原来的方法调用:

GetObjectsForExportImpl(
   long                  lExportOptions, 
   __int64               numIdProject, 
   XExportSets           &exportSets, 
   long                  lClientId, 
   CComPtr<IEPIPDServer> ptrIPDServer,
   FILE                  *fp
   )

到您随后的递归调用:

hr = pExportObject->GetObjectsForExportImpl(
                         lExportOptions,
                         numIdProject,
                         exportSets,
                         lClientId,
                         ptrIPDServer,
                         fp);

除非它们之间发生了一些神奇的事情,否则您只是用自己的一组参数重新调用该方法。我怀疑您的意思是在其中放入“exportLocal”而不是“exportSets”,否则,exportLocal.setShared.begin() 的意义何在?您将继续重新创建一个新的 exportLocal,告诉它开始,递归等等。

简而言之,我认为问题是编码错误,而不是递归问题。

附带说明 - 你能想出一种方法让它成为循环而不是递归吗?循环几乎总是更快、更简单、更容易理解和快速调试。

【讨论】:

  • 实际上我们正在复制本地导出集,即 exportlocal 到 exportSets 。我错过了 for 循环中的一条语句。 exportSets.setShared.insert(numId);是的,我需要考虑一种迭代方式
  • 是否值得将这条线放入问题中?这样我们就可以看到 exportLocal 和 exportSets 之间的关系了。
【解决方案4】:

抱歉 - 我不太懂 C++,但我可以帮上忙。如果您可以使用下标表示法来访问元素,并且您有指向父元素的指针,则可以使用堆栈进行深度优先遍历并避免递归的不重要成本。以下是一些您应该能够翻译的 C# 代码:

Stack<int> childIndex = new Stack<int>();
childIndex.Push(0);

TreeNode<Folder> workingFolder = GetNodeById(folderId);
TreeNode<Folder> returnFolder = ShallowClone(workingFolder);

while (childIndex.Count > 0) {
    int idx = childIndex.Peek();
    if (idx < workingFolder.Children.Count) {
        visit(workingFolder.Children[idx]);

        // increment count for this level
        childIndex.Pop();
        childIndex.Push(idx + 1);

        // replace current working folders and push new index
        workingFolder = workingFolder.Children[idx];
        returnFolder = f;
        childIndex.Push(0);
    } else {
        // no (more) children
        workingFolder = (workingFolder.Parent == null ? workingFolder : workingFolder.Parent);
        returnFolder = (returnFolder.Parent == null ? returnFolder : returnFolder.Parent);
        childIndex.Pop();
    }
}

【讨论】:

    【解决方案5】:

    如果您的递归占用了 2GB 内存,那么您可能会遇到任何类型的内存数据结构的问题。您能否发布一些代码,以便我们更好地了解您在做什么?

    一个想法可能是在您构建 Set 时使用数据库表来存储它。您可以为每个递归插入一条新记录,并且该记录可以具有对表中父记录的 FK 引用。这样,您可以通过跟踪记录中的父引用来上下递归。

    【讨论】:

      【解决方案6】:

      我认为递归与您的问题没有太大关系。问题仅仅是你创建的数据很大。转向迭代解决方案对此无济于事。正如已经指出的那样,在遍历时写入输出,而不是将其存储在内存结构中。

      但是,您可以通过在递归调用中传递指针而不是整个结构来最小化堆栈上的内容。

      【讨论】:

      • 使用基于磁盘的方法,我能够导出到内存中高达 13 GB 的文件。但超出这个限制它失败了
      猜你喜欢
      • 2017-03-15
      • 1970-01-01
      • 2013-12-12
      • 1970-01-01
      • 1970-01-01
      • 2011-05-05
      • 2014-11-01
      • 2012-06-15
      • 2016-05-05
      相关资源
      最近更新 更多