元になった記事はこれ。
パイプライン一つ一つがリンクドリスト状のStream
のインスタンスで構成されているということで、どれくらいの長さのストリームパイプラインを書くとOutOfMemoryError
が発生するのか試してみようと思った。
コードはこれ
import java.util.Map; import java.util.HashMap; import java.util.stream.IntStream; import java.text.DecimalFormat; public class StreamMemorySample { public static void main(String[] args) { Runtime rt = Runtime.getRuntime(); Map<Integer, IntStream> streams = new HashMap<>(); for(int i = 0; i < 10; i++) { streams.put(i, IntStream.of(i)); } long before = rt.maxMemory() - rt.freeMemory(); for (long i = 0; i < 1_000_000_000; i++) { final int v = (int)(i / 10); int key = (int)(i % 10); try { streams.computeIfPresent(key, (k, s) -> s.filter(n -> n < v)); } catch(OutOfMemoryError e) { System.out.println(""); System.out.println(i); throw e; } if(i > 0 && i % 1_000_000 == 0) { long free = rt.freeMemory(); long max = rt.maxMemory(); String pip = new DecimalFormat("#,##0").format(i); String used = new DecimalFormat("#,##0").format(max - free); String fr = new DecimalFormat("#,##0").format(free); String one = new DecimalFormat("#,##0").format((max - free - before) / 1_000_000); System.out.printf("\npipe line[%12s] -> used[%12s], per one[%12s], free memory [%12s]\n", pip, used, one, fr); rt.gc(); before = max - free; } } } }
出力はこんな感じ
$ java -Xms512M -Xmx512M StreamMemorySample pipe line[ 1,000,000] -> used[ 91,626,464], per one[ 86], free memory [ 423,224,352] pipe line[ 2,000,000] -> used[ 179,085,568], per one[ 87], free memory [ 335,765,248] pipe line[ 3,000,000] -> used[ 242,269,736], per one[ 63], free memory [ 272,581,080] pipe line[ 4,000,000] -> used[ 323,051,256], per one[ 80], free memory [ 191,799,560] pipe line[ 5,000,000] -> used[ 400,976,640], per one[ 77], free memory [ 113,874,176] pipe line[ 6,000,000] -> used[ 481,266,056], per one[ 80], free memory [ 33,584,760] 6143117 Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded at java.util.stream.IntPipeline.filter(Unknown Source) at StreamMemorySample.lambda$main$1(StreamMemorySample.java:18) at StreamMemorySample$$Lambda$1/2055281021.apply(Unknown Source) at java.util.HashMap.computeIfPresent(Unknown Source) at StreamMemorySample.main(StreamMemorySample.java:18)
だいたい1つにつき80バイトくらいつかっていて、512M程度与えられていれば610万回くらいはストリームパイプラインつなげても大丈夫そうです。
ただ、そんなに並べると、終端操作呼び出しでスタックオーバーフローが発生します。
Exception in thread "main" java.lang.StackOverflowError at java.util.stream.IntPipeline$9$1.begin(Unknown Source)