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位只會解決你的問題,如果你真的需要那麼多的內存和異常不是由一些錯誤引起的。