System.OutOfMemoryException用于尾递归函数

时间:2013-01-14 16:54:31

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

我已将有问题的代码隔离到此函数(使用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 ->
        ()

我在System.OutOfMemoryException完全5626次迭代后得到h1。但我的系统内存消耗仅为20 percent。 (我有一台非常强大的16 GB机器。)

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

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:5)

我不认为这是一个尾递归问题 - 如果是这样,你会得到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

答案 1 :(得分:5)

OutOfMemoryException通常与您拥有的RAM量没有任何关系。你很可能得到~3 GB,因为你的代码是以32位进程运行的。但是如果你真的需要那么多内存并且异常不是由某些bug造成的,那么将它切换到64位只会解决你的问题。