2016-08-06 2 views
1

Пусть распределения Я несколько крупных объектов (например, вектор размера N, который может быть очень большим) и выполнить последовательность операций м на нем:Глубокие стеки функционального программирования предотвращают сбор мусора в JVM?

fm(.. f3(f2(f1(vec)))) 

с каждым возвращающей коллекцию размера N.

Для простоты будем считать, не каждый е достаточно просто

def f5(vec: Vector[Int]) = { gc(); f6( vec.map(_+1)) } 

Так, больше VEC не имеет будущие ссылки в точке, где производится каждый последующий вызов. (VEC параметра f1 никогда не используются после того, как f2 вводится, и так далее, для каждого вызова)

Однако, поскольку большинство JVMs не уменьшают ссылки до тех пор, раскручивается стек (AFAIK), это не моя программа требуется к потребляют память NxM. Для сравнения в следующем стиле только 2XM требуется (и в меньшей степени в других реализациях)

var vec:Vector[Int] = ... 

    for (f <- F) { 
    vec = f(vec) 
    gc() 
    } 

ли существует та же проблема для хвостовых рекурсивных методов?

Это не просто академическое упражнение - в некоторых типах проблем с большими данными мы можем выбрать N, чтобы наша программа полностью вписывалась в ОЗУ. В этом случае я должен быть обеспокоен тем, что один стиль конвейерной обработки предпочтительнее другого?

+0

Почему бы не использовать 'view', если вы хотите сохранить память? –

+0

@VictorMoroz или даже лучше, замените вектор итератором. Но это не вопрос. Долговечные ссылки на стеки предотвращают GC? – user48956

ответ

2

Прежде всего, ваш вопрос содержит серьезное заблуждение и пример катастрофически плохого кодирования.

Однако, поскольку большинство JVMs не уменьшаем ссылки до тех пор, раскручивается стек (AFAIK) ...

На самом деле нет господствующих JVMs, которые используют подсчет ссылок на ссылки на всех. Вместо этого все они используют алгоритмы выделения, копирования или генерации некоторого рода, которые не полагаются на подсчет ссылок.

Следующего это:

def f5(vec: Vector[Int]) = { gc(); f6( vec.map(_+1)) } 

Я думаю, что вы пытаетесь «сила» сбор мусора с gc() вызова. Не делайте этого: это ужасно неэффективно. И даже если вы только занимаетесь исследованием поведения управления памятью, вы, скорее всего, искажаете это поведение в той мере, в какой то, что вы видите, НЕ является представителем обычного кода Scala.

Сказав это, ответ в основном да. Если ваша функция Scala не может быть оптимизирована с хвостовым оппонентом, существует вероятность глубокой рекурсии, которая может вызвать проблемы с хранением мусора. Единственный «выход» был бы, если бы JIT-компилятор смог сообщить GC, что определенные переменные были «мертвы» в определенных точках вызова метода. Я не знаю, может ли HotSpot JITs/GCs это сделать.

(Я думаю, еще один способ сделать это было бы для компилятора Scala явно назначить null мертвым ссылочным переменным, но это имеет потенциальные проблемы с производительностью, если у вас нет проблемы с удержанием мусора!)

2

Чтобы добавить @ ответ StephenC в

Я не знаю, если HotSpot JITs/ШС может сделать это.

Hotspot jit может анализировать жизнеспособность внутри метода и считать локальные переменные недоступными, даже если кадр все еще находится в стеке. Вот почему JDK9 вводит Reference.reachabilityFence, при некоторых условиях даже this может стать недоступным при выполнении метода-члена этого экземпляра.

Но эта оптимизация применяется только тогда, когда в потоке управления действительно нет ничего, что может прочитать эту локальную переменную, например. нет окончательных блоков или выходов монитора. Таким образом, это будет зависеть от байт-кода, созданного scala.

+0

Обратите внимание, что в отличие от 'javac', который является довольно немым компилятором (когда дело доходит до оптимизации, очевидно, что тип inferencer, тип checker, разрешение перегрузки и т. Д. Очень сложны),' scalac' выполняет сложные статические оптимизации. Однако я не уверен, что любая из этих оптимизаций может помочь с этой конкретной проблемой. Обратите также внимание на то, что существует несколько реализаций Scala, и поведение памяти (в основном) остается отдельным разработчиком, за исключением того, что спецификация требует устранения прямой хвостовой рекурсии. Например. Scala-native имеет или скоро получит полную TCO. –

+0

ECMAScript 2015 имеет полную совокупную стоимость владения, поэтому в зависимости от конкретной кодировки, выбранной Scala.js, и скомпилированный код работает на движке ECMAScript 5 или ECMAScript 2015, код Scala может также обладать полной совокупной стоимостью владения, что в данном случае означает кадр стека повторно используется, и промежуточные объекты становятся недоступными немедленно. Конечно, в вопросе конкретно упоминается JVM. Однако JV JVM имеет полную совокупную стоимость владения. –

+0

Многие механизмы javascript на самом деле не реализуют TCO: https://kangax.github.io/compat-table/es6/ – the8472

0

Вызовы в вашем примере - это хвостовые звонки. У них действительно не должно быть фрейма стека, выделенного вообще. Тем не менее, по разным неудачным причинам спецификация языка Scala не предусматривает правильные хвостовые вызовы, и по столь же неудачным причинам реализация Scala-JVM не выполняет оптимизацию Tail Call Optimization.

Однако некоторые JVM имеют TCO, например. JV JVM выполняет TCO, и поэтому не должно быть никаких дополнительных фреймов стека, что делает промежуточные объекты недоступными, как только произойдет следующий вызов хвоста. Даже JVM, которые имеют , но не, имеют TCO, которые могут выполнять различные статические (анализ на анализ, анализ жизнеспособности) или динамические (обнаружение escape, например, Azul Zing JVM), которые могут или не могут помочь в этом случае.

Есть также другие реализации Scala: Scala.js не выполняет TCO, насколько я знаю, но он компилирует в ECMAScript, и по состоянию на ECMAScript 2015, ECMAScript делает имеет Правильную хвостовую рекурсию, так до тех пор, так как кодирование вызовов метода Scala заканчивается вызовом функций ECMAScript, то соответствующий ECMAScript 2015 механизм , соответствующий стандартам, должен устранить хвостовые вызовы Scala.

Scala-native в настоящее время не выполняет TCO, но это будет в будущем.

Смежные вопросы