【问题标题】:System.OutOfMemoryException for a tail recursive function尾递归函数的 System.OutOfMemoryException
【发布时间】:2012-12-28 16:16:42
【问题描述】:

我已将有问题的代码隔离到此函数(使用 ASP.NET 的 Membership 类):

let dbctx = DBSchema.GetDataContext()
let rec h1 (is2_ : int) (ie2_ : int) : unit =
    match is2_ >= ie2_ with
    | true ->
        let st2 = query {
            for row in dbctx.Tbl_Students do
            where (row.Id = is2_)
            head}
        let l2 =
            Membership.FindUsersByEmail (st2.Email_address)
            |> Seq.cast<_>
            |> Seq.length
        match l2 >= 1 with
        | true -> 
            ()
        | false ->
            Membership.CreateUser (st2.Email_address, password, st2.Email_address)
            |> ignore
        h1 (is2_ - 1) ie2_
    | false ->
        ()

5626h1 迭代之后,我得到了一个System.OutOfMemoryException。但是我系统的内存消耗只有20 percent。 (我有一台非常强大的 16 GB 机器。)

为什么上面的函数会溢出堆栈?不是递归写成tail吗?

提前感谢您的帮助。

【问题讨论】:

  • 您是否在调试模式下运行?如果是这样,则禁用尾调用。在发布模式下尝试您的代码。
  • 我会用Seq.isEmpty重写最后一部分。无需枚举整个序列。
  • @Daniel 是这样吗?我在项目中有许多其他尾递归函数,它们在相同的调试模式下遍历更深而不会引发错误。不过,我会检查这是否是问题所在。感谢您的建议。
  • @pad,感谢您的建议。我已经进行了更改(在我的实际代码中)。
  • 是否有可能是数据上下文被重用和积累数据。我希望这条线是 - 让 dbctx() = DBSchema.GetDataContext() 代替,每次都创建一个新的数据上下文。

标签: f# asp.net-membership tail-recursion tail-call-optimization


【解决方案1】:

我不认为这是一个尾递归问题——如果是这样,你会得到一个StackOverflowException 而不是OutOfMemoryException。请注意,即使您的计算机中有 16GB 的内存,您的程序正在执行的进程也可能仅限于较小的内存量。 IIRC,对于 .NET 框架版本和操作系统版本的某些组合,它是 3GB——这可以解释为什么当内存使用量达到约 20%(16GB 的 20% = 3.2GB)时进程会崩溃。

我不知道它有多大帮助,但您可以简化代码以避免创建一些不必要的序列:

let dbctx = DBSchema.GetDataContext()
let rec h1 (is2_ : int) (ie2_ : int) : unit =
    if is2_ >= ie2_ then
        let st2 = query {
            for row in dbctx.Tbl_Students do
            where (row.Id = is2_)
            head }
        let existingUsers = Membership.FindUsersByEmail st2.Email_address
        if existingUsers.Count < 1 then
            Membership.CreateUser (st2.Email_address, password, st2.Email_address)
            |> ignore

        h1 (is2_ - 1) ie2_

编辑:这是上一个问题的链接,其中包含有关 .NET 框架和操作系统版本的某些版本的 CLR 内存限制的详细信息:Is there a memory limit for a single .NET process

【讨论】:

    【解决方案2】:

    OutOfMemoryException 通常与您拥有的 RAM 数量没有任何关系。您最有可能以大约 3 GB 的速度获得它,因为您的代码作为 32 位进程运行。但是,只有在您确实需要那么多内存并且异常不是由某些错误引起的情况下,将其切换到 64 位才能解决您的问题。

    【讨论】:

      猜你喜欢
      • 2019-09-14
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 2016-06-13
      • 1970-01-01
      • 1970-01-01
      • 2016-01-06
      • 2020-09-16
      相关资源
      最近更新 更多