为什么对象在执行 Java 流终端操作时没有被垃圾回收?

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

我试图了解是什么保存了对对象的引用,以便在执行 Java 流终端操作时它们不符合垃圾回收条件?

这是我的测试代码

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 的 finalize 方法。

我的想法是,一旦 .map(largeObject -> new Object()) 被执行,那么没有任何东西持有对 LargeObject 的强引用并且它有资格进行垃圾收集。

为什么这不会发生?也许确实可以做点什么?

这与终结和gc无关。您应该提到您处于 java-9 或更高级别(我假设是这样,但事实应该如此)。我会简化这个:

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

    System.out.println(howMany);

由于初始 Stream 的大小已知且未更改,因此您的 peekmap 根本不会执行(因此不会产生垃圾)。看到这个:

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());
}

用java-8编译相同的代码,你会看到不同的画面。