F # компилятор поддерживает мертвые объекты - PullRequest
8 голосов
/ 12 июня 2011

Я реализую некоторые алгоритмы, которые работают с большими данными (~ 250 МБ - 1 ГБ). Для этого мне понадобился цикл, чтобы сделать некоторые тесты. Однако в процессе я узнаю, что F # делает некоторые неприятные вещи, которые, я надеюсь, некоторые из вас смогут уточнить.

Вот мой код (описание проблемы ниже):

open System

for i = 1 to 10 do
    Array2D.zeroCreate 10000 10000 |> ignore    
    printfn "%d" (GC.GetTotalMemory(true)) 

Array2D.zeroCreate 10000 10000 |> ignore
// should force a garbage collection, and GC.Collect() doesn't help either
printfn "%d" (GC.GetTotalMemory(true))
Array2D.zeroCreate 10000 10000 |> ignore    
printfn "%d" (GC.GetTotalMemory(true))
Array2D.zeroCreate 10000 10000 |> ignore    
printfn "%d" (GC.GetTotalMemory(true))
Array2D.zeroCreate 10000 10000 |> ignore    
printfn "%d" (GC.GetTotalMemory(true))

Console.ReadLine() |> ignore

Здесь вывод будет выглядеть так:

54000
54000
54000
54000
54000
54000
54000
54000
54000
54000
400000000
800000000
1200000000

Out of memory exception

Итак, в цикле F # отбрасывает результат, но когда я не в цикле, F # будет хранить ссылки на «мертвые данные» (я посмотрел в IL, и, очевидно, класс Program получает поля для этих данных ). Зачем? И можно это исправить?

Этот код запускается вне Visual Studio и в режиме выпуска.

1 Ответ

17 голосов
/ 12 июня 2011

Причиной такого поведения является то, что компилятор F # ведет себя иначе в глобальной области видимости, чем в локальной области видимости.Переменная, объявленная в глобальной области видимости, превращается в статическое поле.Объявление модуля - это статический класс с объявлениями let, скомпилированными как поля / свойства / методы.

Самый простой способ решить проблему - написать код в функции:

let main () =    
  Array2D.zeroCreate 10000 10000 |> ignore    
  printfn "%d" (GC.GetTotalMemory(true))
  Array2D.zeroCreate 10000 10000 |> ignore    
  printfn "%d" (GC.GetTotalMemory(true))
  // (...)
  Console.ReadLine() |> ignore

main ()

... но почему компилятор объявляет поля, когда вы не используете значение и просто ignore оно?Это довольно интересно - функция ignore - это очень простая функция, которая встроена, когда вы ее используете.Декларация let inline ignore _ = ().При вставке функции компилятор объявляет некоторые переменные (для хранения аргументов функции).

Итак, еще один способ исправить это - пропустить ignore и написать:

Array2D.zeroCreate 10000 10000 
printfn "%d" (GC.GetTotalMemory(true))
Array2D.zeroCreate 10000 10000 
printfn "%d" (GC.GetTotalMemory(true))
// (...)

Вы получите некоторые предупреждения компилятора, потому что результат выражения не unit,но это будет работать.Однако использование некоторой функции и написание кода в локальной области видимости, вероятно, более надежно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...