简体   繁体   中英

Why objects not being garbage collected while executing Java Stream terminal operation?

I'm trying to understand what holds the reference to the objects so that they are not eligible for garbage collection when Java Stream terminal operation is being executed?

Here is my test code

import java.util.stream.IntStream;
import java.util.stream.Stream;

class Scratch {
   public static void main(String[] args) {
       Stream<LargeObject> objectStream = IntStream.rangeClosed(0, 1000000).mapToObj(LargeObject::new);
       objectStream.peek(obj -> {
           try {
               Thread.sleep(1);

               if (obj.i % 100 == 0) {
                   System.out.println("Processed: " + obj.i);
               }

               if (obj.i % 10000 == 0) {
                   System.out.println("Calling GC");
                   System.gc();
               } 

           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       })
         .map(largeObject -> new Object())
         .count();
   }

   private static class LargeObject {
       private final int i;
       private final byte[] alloc = new byte[1024];

       private LargeObject(int i) {
           this.i = i;
       }

       @Override
       protected void finalize() throws Throwable {
           super.finalize();
           System.out.println("" + i + " collected");
       }
   }
 }

LargetObject's finalize method is never called.

My thought was that once.map(largeObject -> new Object()) is executed then nothing holds a strong reference to the LargeObject and it becomes eligible for garbage collection.

Why this does not happen? And maybe something could be done indeed?

This has nothing to do with finalization and gc . You should have mentioned that you are on java-9 or higher (I assume so, but it should be the case). I'll simplify this:

    long howMany = List.of(1, 2, 3)
                       .stream()
                       .map(x -> {
                           System.out.println("mapping = " + x);
                           return x;
                       })
                       .count();

    System.out.println(howMany);

Since the initial Stream has a known size, and that is not altered, your peek and map are not executed at all (as such no garbage generated). See this:

public static void main(String[] args) {

    List<Integer> list = List.of(1, 2, 3);
    Stream<Integer> one = isSized(list.stream());
    Stream<Integer> two = isSized(one.map(x -> {
        System.out.println("mapping = " + x);
        return x;
    }));

    long count = isSized(two).count();

    System.out.println(count);
}

private static Stream<Integer> isSized(Stream<Integer> stream) {
    Spliterator<Integer> sp = stream.spliterator();
    System.out.println(sp.hasCharacteristics(Spliterator.SIZED));
    return StreamSupport.stream(sp, stream.isParallel());
}

Compile the same code with java-8 and you will see a different picture.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM