代码之家  ›  专栏  ›  技术社区  ›  Juliet

递归序列会泄漏内存吗?

  •  10
  • Juliet  · 技术社区  · 17 年前

    我喜欢递归地定义序列,如下所示:

    let rec startFrom x =
        seq {
            yield x;
            yield! startFrom (x + 1)
        }
    

    我不确定像这样的递归序列是否应该在实践中使用。这个 yield! 出现 是尾部递归的,但我不是100%确定,因为它是从另一个IEnumerable内部调用的。在我看来,代码在每次调用时都会创建一个IEnumerable实例,而不关闭它,这实际上会使这个函数泄漏内存。

    [编辑添加]:我正在NProf中摸索一个答案,但我认为获得关于递归序列实现的技术解释会很有帮助。

    3 回复  |  直到 17 年前
        1
  •  7
  •   Community Mohan Dere    9 年前

    我现在正在工作,所以我正在寻找比Beta1稍新的位,但在我的发布模式中的框中,然后在使用.Net Reflector查看编译代码时,似乎这两个

    let rec startFromA x =    
        seq {        
            yield x     
            yield! startFromA (x + 1)    
        }
    
    let startFromB x =    
        let z = ref x
        seq {        
            while true do
                yield !z
                incr z
        }
    

    public class CSharpExample
    {
        public static IEnumerable<int> StartFrom(int x)
        {
            while (true)
            {
                yield return x;
                x++;
            }
        }
    }
    

    (例如,我在我的盒子上运行了所有三个版本并打印了第一百万个结果,每个版本大约花费1.3秒+/-1秒)。(我没有做任何内存分析;可能我遗漏了一些重要的内容。)

    简言之,我不会花太多时间思考这样的问题,除非你衡量并看到一个问题。

    我意识到我没有真正回答这个问题。。。我认为简短的回答是“不,它不会泄漏”。(所有“无限”IEnumerables(带有缓存的备份存储)“泄漏”(取决于您如何定义“泄漏”)有一种特殊的含义,请参阅

    Avoiding stack overflow (with F# infinite sequences of sequences)

        2
  •  -2
  •   Andrew Hare    17 年前

    .NET应用程序不会以这种方式“泄漏”内存。即使您正在创建许多对象,垃圾收集也会将没有根的对象释放到应用程序本身。

    NET中的内存泄漏通常以应用程序中使用的非托管资源(数据库连接、内存流等)的形式出现。在这样的情况下,创建多个对象然后放弃它们并不被视为内存泄漏,因为垃圾收集器能够释放内存。

        3
  •  -2
  •   em70    17 年前

    它不会泄漏任何内存,只会生成一个无限序列,但由于序列是IEnumerables,所以您可以枚举它们而无需考虑内存问题。 请记住,在调试模式下,为了允许完全调试,可能会禁用尾部调用优化,但在发行版中不会出现任何问题。