Про потоки (не мыслей)
Dec. 1st, 2012 03:49 pmИз дискуссии с
sassa_nf.
В java локальные переменные метода не собираются сборщиком мусора, пока мы не выйдем из метода (наверх, разумеется, а не глубже). Это важно, потому что Scala'вские Stream из-за этого часто становятся бесполезны.
Вот имеем мы некий Stream, который любят сравнивать с Haskell'евским списком. И начинаем его обрабатывать. А обработка через некоторое время завешается с OutOfMemoryError:
А теперь перепишем это так:
и обработка у нас будет вестись вечно.
А всё потому, что первый способ генерит astore, который создаёт локальную переменную. А второй — нет.
UPD:
sassa_nf всё же заставил java работать правильно. За что ему отдельное спасибо!
В 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:
no subject
Date: 2012-12-01 11:56 am (UTC)no subject
Date: 2012-12-01 12:07 pm (UTC)Т.е. в первом примере сгенерится astore и в нужном месте (где foreach, если знаешь Scala, или где <-, если не знаешь) будет вызван aload.
Во втором примере в том месте, где в первом был aload будет сделан непосредственный вызов — создание Stream-а. И вот он уже будет собираться gc.
no subject
Date: 2012-12-01 12:47 pm (UTC)no subject
Date: 2012-12-01 01:00 pm (UTC)вообще любоей вменяемый компилятор делает анализ живости, поэтому если локальная переменная с какого-то момента не используется, то ее GC и не должен видеть.
no subject
Date: 2012-12-01 01:07 pm (UTC)no subject
Date: 2012-12-01 02:45 pm (UTC)no subject
Date: 2012-12-01 04:26 pm (UTC)no subject
Date: 2012-12-01 05:26 pm (UTC)Ну вот для этого:
object X { def main(xs: Array[String]) = { for (x <- Stream.continually(1)) {} // первый случай val xs = Stream.continually(1) // второй случай for (x <- xs) {} } }байткод будет
public final class X$ extends java.lang.Object implements scala.ScalaObject{ public static final X$ MODULE$; public static {}; Code: 0: new #9; //class X$ 3: invokespecial #12; //Method "":()V 6: return public void main(java.lang.String[]); Code: 0: getstatic #19; //Field scala/package$.MODULE$:Lscala/package$; 3: invokevirtual #24; //Method scala/package$.Stream:()Lscala/collection/immutable/Stream$; 6: new #26; //class X$$anonfun$main$1 9: dup 10: invokespecial #27; //Method X$$anonfun$main$1."":()V 13: invokevirtual #33; //Method scala/collection/immutable/Stream$.continually:(Lscala/Function0;)Lscala/collection/immutable/Stream; 16: new #35; //class X$$anonfun$main$2 19: dup 20: invokespecial #36; //Method X$$anonfun$main$2."":()V 23: invokevirtual #42; //Method scala/collection/immutable/Stream.foreach:(Lscala/Function1;)V 26: getstatic #19; //Field scala/package$.MODULE$:Lscala/package$; 29: invokevirtual #24; //Method scala/package$.Stream:()Lscala/collection/immutable/Stream$; 32: new #44; //class X$$anonfun$1 35: dup 36: invokespecial #45; //Method X$$anonfun$1."":()V 39: invokevirtual #33; //Method scala/collection/immutable/Stream$.continually:(Lscala/Function0;)Lscala/collection/immutable/Stream; 42: astore_2 43: aload_2 44: new #47; //class X$$anonfun$main$3 47: dup 48: invokespecial #48; //Method X$$anonfun$main$3."":()V 51: invokevirtual #42; //Method scala/collection/immutable/Stream.foreach:(Lscala/Function1;)V 54: return }Обрати внимание на 42-43 строчки.
no subject
Date: 2012-12-01 05:28 pm (UTC)no subject
Date: 2012-12-01 05:40 pm (UTC)no subject
Date: 2012-12-01 05:45 pm (UTC)no subject
Date: 2012-12-01 05:47 pm (UTC)до того как этот foreach вернется поток точно не помрет в обоих случаях, а после того как вернется... ну в интерпретаторе он допустим застрянет в локале ненадолго, но я более чем уверен, что для C1/C2 локал мертв после 43й строчки.
no subject
Date: 2012-12-01 05:54 pm (UTC)https://github.com/scalaz/scalaz/blob/master/core/src/main/scala/scalaz/EphemeralStream.scala
Советы использовать tailrec, чтобы head не сохранялся, мне не особо понравились.
no subject
Date: 2012-12-01 05:59 pm (UTC)Закомментируй первый случай, скомпиль, запусти. Убедись, что падает.
Раскомментируй, скомпиль, запусти. Убедись, что вертится.
no subject
Date: 2012-12-01 06:05 pm (UTC)Так там weak reference! У этого-то, конечно, будут чиститься все tail (кроме самого первого Stream, разумеется).
no subject
Date: 2012-12-01 06:17 pm (UTC)Опять локальные переменные, опять out-of-memory.
no subject
Date: 2012-12-01 06:39 pm (UTC)чудеса! совершенно непонятно зачем и где они его удерживают. я бы не стал.
ради интереса хотел посмотреть ассемблерный код, но на маке это надо через скакалочку прыгать, оказывается...
(а Stream.foreach оказывается свой this перезаписывает. поэтому он там и не застревает как я ожидал).
no subject
Date: 2012-12-01 06:46 pm (UTC)no subject
Date: 2012-12-01 06:47 pm (UTC)foreach — тоже угу, хвостато-рекурсивный.
no subject
Date: 2012-12-01 06:53 pm (UTC)Прийдется возращаться к рекурсии ;)
no subject
Date: 2012-12-01 10:21 pm (UTC)no subject
Date: 2012-12-02 04:06 am (UTC)no subject
Date: 2012-12-02 09:29 am (UTC)no subject
Date: 2012-12-02 09:33 am (UTC)no subject
Date: 2012-12-02 09:38 am (UTC)У тебя два использования n. Он просто обязан хранить весь список.
no subject
Date: 2012-12-02 09:38 am (UTC)no subject
Date: 2012-12-02 09:48 am (UTC)no subject
Date: 2012-12-03 08:59 am (UTC)no subject
Date: 2012-12-03 09:28 am (UTC)Смысл такой, что на некоторых jvm да с некоторыми ключами jit оптимизирует хранение переменной (регистр, параметр следующей функции, х.з.). И gc её всё-таки убивает.
no subject
Date: 2012-12-03 09:51 am (UTC)ну хоть где-то починили, хотя я прямо так скажем ожидал, что с версии 0 должно нормально это работать :-)
[хорошо хоть мое шестое чувство меня не подвело :-)]
no subject
Date: 2012-12-03 12:00 pm (UTC)