В вашем методе
static int nth(final int n, final LazyishSeq lazySeq) {
return drop(n, lazySeq).head();
}
переменная параметра lazySeq
содержит ссылку на первый элемент вашей последовательности в течение всей операции drop
. Это предотвращает сбор мусора во всей последовательности.
В отличие от
public void dropTest() {
assertThat( drop(N, naturals()).head(), is(N+1));
}
первый элемент вашей последовательности возвращается naturals()
и напрямую передается на вызов drop
, таким образом удаляется из стека операндов и не существует во время выполнения drop
.
Ваша попытка установить переменную параметра на null
, т.е.
static int nth(final int n, /*final*/ LazyishSeq lazySeqArg) {
final LazyishSeq lazySeqLocal = lazySeqArg;
lazySeqArg = null;
return drop(n,lazySeqLocal).head();
}
не помогает, так как теперь переменная lazySeqArg
равна null
, но lazySeqLocal
содержит ссылку на первый элемент.
Локальная переменная не предотвращает сборку мусора в целом, сбор неиспользуемых объектов разрешен , но это не означает, что конкретная реализация способна это сделать.
В случае HotSpot JVM только оптимизированный код избавит от таких неиспользуемых ссылок. Но здесь nth
не является горячей точкой, так как тяжелые вещи происходят в методе drop
.
Это причина, по которой такая же проблема не возникает в методе drop
, хотя она также содержит ссылку на первый элемент в своей переменной параметра. Метод drop
содержит цикл, выполняющий реальную работу, следовательно, JVM может с большой вероятностью оптимизировать его, что может привести к исключению неиспользуемых переменных, что позволит собирать уже обработанную часть последовательности.
Существует множество факторов, которые могут повлиять на оптимизацию JVM. Помимо другой формы кода, кажется, что быстрое выделение памяти во время неоптимизированной фазы может также уменьшить улучшения оптимизатора. Действительно, когда я запускаю с -Xcompile
, чтобы вообще запретить интерпретируемое выполнение, оба варианта запускаются успешно, даже int N = (int)1e9
больше не проблема. Конечно, принудительная компиляция увеличивает время запуска.
Я должен признать, что я не понимаю, почему смешанный режим выполняет , что намного хуже, и я буду исследовать дальше. Но обычно вы должны знать, что эффективность сборщика мусора зависит от реализации, поэтому объекты, собранные в одной среде, могут оставаться в памяти в другой.