Entry tags:
Про потоки (не мыслей)
Из дискуссии с
sassa_nf.
В java локальные переменные метода не собираются сборщиком мусора, пока мы не выйдем из метода (наверх, разумеется, а не глубже). Это важно, потому что Scala'вские Stream из-за этого часто становятся бесполезны.
Вот имеем мы некий Stream, который любят сравнивать с Haskell'евским списком. И начинаем его обрабатывать. А обработка через некоторое время завешается с OutOfMemoryError:
А теперь перепишем это так:
и обработка у нас будет вестись вечно.
А всё потому, что первый способ генерит astore, который создаёт локальную переменную. А второй — нет.
UPD:
sassa_nf всё же заставил java работать правильно. За что ему отдельное спасибо!
![[livejournal.com profile]](https://www.dreamwidth.org/img/external/lj-userinfo.gif)
В java локальные переменные метода не собираются сборщиком мусора, пока мы не выйдем из метода (наверх, разумеется, а не глубже). Это важно, потому что Scala'вские Stream из-за этого часто становятся бесполезны.
Вот имеем мы некий Stream, который любят сравнивать с Haskell'евским списком. И начинаем его обрабатывать. А обработка через некоторое время завешается с OutOfMemoryError:
val messages = Stream continually nextMessage for (message <- messages) doSomething(message)
А теперь перепишем это так:
for (message <- Stream continually nextMessage) doSomething(message)
и обработка у нас будет вестись вечно.
А всё потому, что первый способ генерит astore, который создаёт локальную переменную. А второй — нет.
UPD:
![[livejournal.com profile]](https://www.dreamwidth.org/img/external/lj-userinfo.gif)
no subject
no subject
Т.е. в первом примере сгенерится astore и в нужном месте (где foreach, если знаешь Scala, или где <-, если не знаешь) будет вызван aload.
Во втором примере в том месте, где в первом был aload будет сделан непосредственный вызов — создание Stream-а. И вот он уже будет собираться gc.
no subject
no subject
no subject
no subject
no subject
no subject
вообще любоей вменяемый компилятор делает анализ живости, поэтому если локальная переменная с какого-то момента не используется, то ее GC и не должен видеть.
no subject
no subject
no subject
Ну вот для этого:
байткод будет
Обрати внимание на 42-43 строчки.
no subject
до того как этот foreach вернется поток точно не помрет в обоих случаях, а после того как вернется... ну в интерпретаторе он допустим застрянет в локале ненадолго, но я более чем уверен, что для C1/C2 локал мертв после 43й строчки.
no subject
Закомментируй первый случай, скомпиль, запусти. Убедись, что падает.
Раскомментируй, скомпиль, запусти. Убедись, что вертится.
no subject
чудеса! совершенно непонятно зачем и где они его удерживают. я бы не стал.
ради интереса хотел посмотреть ассемблерный код, но на маке это надо через скакалочку прыгать, оказывается...
(а Stream.foreach оказывается свой this перезаписывает. поэтому он там и не застревает как я ожидал).
no subject
foreach — тоже угу, хвостато-рекурсивный.
no subject
no subject
Смысл такой, что на некоторых jvm да с некоторыми ключами jit оптимизирует хранение переменной (регистр, параметр следующей функции, х.з.). И gc её всё-таки убивает.
no subject
ну хоть где-то починили, хотя я прямо так скажем ожидал, что с версии 0 должно нормально это работать :-)
[хорошо хоть мое шестое чувство меня не подвело :-)]
no subject
no subject
https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/EphemeralStream.scala
Советы использовать tailrec, чтобы head не сохранялся, мне не особо понравились.
no subject
Так там weak reference! У этого-то, конечно, будут чиститься все tail (кроме самого первого Stream, разумеется).
no subject
Опять локальные переменные, опять out-of-memory.
no subject
Прийдется возращаться к рекурсии ;)
no subject
no subject
no subject
no subject
У тебя два использования n. Он просто обязан хранить весь список.
no subject
no subject
no subject
no subject