为什么对象在执行 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 的大小已知且未更改,因此您的 peek
和 map
根本不会执行(因此不会产生垃圾)。看到这个:
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编译相同的代码,你会看到不同的画面。
我试图了解是什么保存了对对象的引用,以便在执行 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 的大小已知且未更改,因此您的 peek
和 map
根本不会执行(因此不会产生垃圾)。看到这个:
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编译相同的代码,你会看到不同的画面。