System.OutOfMemoryException для хвостовой рекурсивной функции

Я выделил проблемный код для этой функции (которая использует класс Membership ASP.NET):

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 ГБ.)

Почему вышеупомянутая функция должна переполнять стек? Не написано ли хвост рекурсивно?

Заранее спасибо за помощь.

2 ответа

Решение

Я не думаю, что это проблема хвостовой рекурсии - если так, то вы получите StackruException вместо OutOfMemoryException, Обратите внимание, что даже если у вас есть 16 ГБ памяти на вашем компьютере, процесс, в котором выполняется ваша программа, может быть ограничен меньшим объемом памяти. IIRC, это 3 ГБ для некоторых комбинаций версии.NET Framework и версии ОС - это объясняет, почему происходит сбой процесса, когда вы используете ~20% памяти (20% из 16 ГБ = 3,2 ГБ).

Я не знаю, насколько это поможет, но вы можете упростить свой код, чтобы избежать создания ненужных последовательностей:

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 Framework и версий ОС: существует ли ограничение памяти для одного процесса.NET

OutOfMemoryException обычно не имеет никакого отношения к объему оперативной памяти, которая у вас есть. Вы получите его на ~3 ГБ, скорее всего, потому что ваш код работает как 32-битный процесс. Но переключение на 64-разрядную версию решит вашу проблему только в том случае, если вам действительно нужно столько памяти, а исключение не вызвано какой-либо ошибкой.

Другие вопросы по тегам