title: Java 8 stream objects significant memory usage
body: <p>In looking at some profiling results, I noticed that using streams within a tight loop (used instead of another nested loop) incurred a significant memory overhead of objects of types <code>java.util.stream.ReferencePipeline</code> and <code>java.util.ArrayList$ArrayListSpliterator</code>. I converted the offending streams to foreach loops, and the memory consumption decreased significantly. </p>
  
  <p>I know that streams make no promises about performing any better than ordinary loops, but I was under the impression that the difference would be negligible. In this case it seemed like it was a 40% increase.</p>
  
  <p>Here is the test class I wrote to isolate the problem. I monitored memory consumption and object allocation with JFR:</p>
  
  <pre><code>import java.util.ArrayList;
  import java.util.List;
  import java.util.Optional;
  import java.util.Random;
  import java.util.function.Predicate;
  
  public class StreamMemoryTest {
  
      private static boolean blackHole = false;
  
      public static List&lt;Integer&gt; getRandListOfSize(int size) {
          ArrayList&lt;Integer&gt; randList = new ArrayList&lt;&gt;(size);
          Random rnGen = new Random();
          for (int i = 0; i &lt; size; i++) {
              randList.add(rnGen.nextInt(100));
          }
          return randList;
      }
  
      public static boolean getIndexOfNothingManualImpl(List&lt;Integer&gt; nums, Predicate&lt;Integer&gt; predicate) {
  
          for (Integer num : nums) {
              // Impossible condition
              if (predicate.test(num)) {
                  return true;
              }
          }
          return false;
      }
  
      public static boolean getIndexOfNothingStreamImpl(List&lt;Integer&gt; nums, Predicate&lt;Integer&gt; predicate) {
          Optional&lt;Integer&gt; first = nums.stream().filter(predicate).findFirst();
          return first.isPresent();
      }
  
      public static void consume(boolean value) {
          blackHole = blackHole &amp;&amp; value;
      }
  
      public static boolean result() {
          return blackHole;
      }
  
      public static void main(String[] args) {
          // 100 million trials
          int numTrials = 100000000;
          System.out.println("Beginning test");
          for (int i = 0; i &lt; numTrials; i++) {
              List&lt;Integer&gt; randomNums = StreamMemoryTest.getRandListOfSize(100);
              consume(StreamMemoryTest.getIndexOfNothingStreamImpl(randomNums, x -&gt; x &lt; 0));
              // or ...
              // consume(StreamMemoryTest.getIndexOfNothingManualImpl(randomNums, x -&gt; x &lt; 0));
              if (randomNums == null) {
                  break;
              }
          }
          System.out.print(StreamMemoryTest.result());
      }
  }
  </code></pre>
  
  <p>Stream implementation:</p>
  
  <p><code>Memory Allocated for TLABs    64.62 GB</code></p>
  
  <pre><code>Class   Average Object Size(bytes)  Total Object Size(bytes)    TLABs   Average TLAB Size(bytes)    Total TLAB Size(bytes)  Pressure(%)
  java.lang.Object[]                          415.974 6,226,712   14,969  2,999,696.432   44,902,455,888  64.711
  java.util.stream.ReferencePipeline$2        64      131,264     2,051   2,902,510.795   5,953,049,640   8.579
  java.util.stream.ReferencePipeline$Head     56      72,744      1,299   3,070,768.043   3,988,927,688   5.749
  java.util.stream.ReferencePipeline$2$1      24      25,128      1,047   3,195,726.449   3,345,925,592   4.822
  java.util.Random                            32      30,976      968     3,041,212.372   2,943,893,576   4.243
  java.util.ArrayList                         24      24,576      1,024   2,720,615.594   2,785,910,368   4.015
  java.util.stream.FindOps$FindSink$OfRef     24      18,864      786     3,369,412.295   2,648,358,064   3.817
  java.util.ArrayList$ArrayListSpliterator    32      14,720      460     3,080,696.209   1,417,120,256   2.042
  </code></pre>
  
  <p>Manual implementation:</p>
  
  <p><code>Memory Allocated for TLABs    46.06 GB</code></p>
  
  <pre><code>Class   Average Object Size(bytes)  Total Object Size(bytes)    TLABs   Average TLAB Size(bytes)    Total TLAB Size(bytes)  Pressure(%)
  java.lang.Object[]      415.961     4,190,392       10,074      4,042,267.769       40,721,805,504  82.33
  java.util.Random        32          32,064          1,002       4,367,131.521       4,375,865,784   8.847
  java.util.ArrayList     24          14,976          624         3,530,601.038       2,203,095,048   4.454
  </code></pre>
  
  <p>Has anyone else encountered issues with the stream objects themselves consuming memory? / Is this a known issue?</p>