2

我已分离出有问题的代码,这个功能(使用ASP.NET的Membership类):的System.OutOfMemoryException一个尾递归函数

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 -> 
     () 

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

为什么上面的函数应该溢出栈?它是不是递归地写尾?

在此先感谢您的帮助。

+3

你在Debug模式下运行吗?如果是这样,尾巴呼叫被禁用。在发布模式下试用您的代码。 – Daniel

+2

我会用'Seq.isEmpty'重写最后一部分。没有必要列举整个序列。 – pad

+0

@丹尼尔是这样吗?我在项目中有许多其他的尾递归函数,它们在相同的调试模式下遍历更大的深度而不会引发错误。不过,我会检查是否是这个问题。感谢您的建议。 – Shredderroy

回答

5

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

我不知道有多少对大家有所帮助,但你可以简化你的代码,以避免产生一些不必要的序列:

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_ 

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

5

OutOfMemoryException通常与您拥有的RAM数量无关。你可以在〜3 GB之内得到它,这很可能是因为你的代码是以32位进程运行的。但切换到64位只会解决你的问题,如果你真的需要那么多的内存和异常不是由一些错误引起的。