当前位置: 首页 > 知识库问答 >
问题:

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

华子航
2023-03-14

我试图理解是什么保存了对对象的引用,使得它们在执行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-

为什么这不会发生?也许真的可以做些什么?

共有1个答案

汪才
2023-03-14

这与最终确定和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);

由于初始流的大小是已知的,并且没有改变,因此根本不会执行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编译相同的代码,你会看到不同的画面。

 类似资料:
  • 问题内容: 是什么决定了垃圾收集器何时真正收集?它是在一定时间之后还是在一定数量的内存用完之后发生的吗?还是还有其他因素? 问题答案: 它在确定是时候运行时运行。在世代垃圾收集器中,一种常见的策略是在第0代内存分配失败时运行收集器。也就是说,每次你分配一小块内存(大块通常直接放置在“旧”代中)时,系统都会检查gen-0堆中是否有足够的可用空间,如果没有,则运行GC释放空间以使分配成功。然后将旧数据

  • 有人能给我解释一下原因吗?

  • 问题内容: 我经常读到,在Sun JVM中,短寿命对象(“相对较新的对象”)比长寿命对象(“相对较旧的对象”)可以更有效地进行垃圾回收。 为什么呢? 这是特定于Sun JVM还是由一般的垃圾回收原理导致? 问题答案: 大多数Java应用程序都会创建Java对象,然后很快将其丢弃。您可以在方法中创建一些对象,然后一旦退出该方法,所有对象都会死亡。大多数应用程序都是以这种方式运行的,并且大多数人倾向于

  • [GC(分配失败)[defnew:10931K->472K(12288K),0.0053905秒]10931K->10712K(39616K),0.0054285秒][times:user=0.00 sys=0.00,real=0.01秒] [GC(分配失败)[defnew:10712k->472k(12288k),0.0057686秒]20952k->20952k(39616k),0.00580

  • 问题内容: 我对可以控制CMS收集器启动时间的两个参数感到困惑: (默认为70%) (默认情况下超过90%) 这些参数的确切含义是什么?收集器什么时候开始(标记阶段)并收集(清扫阶段)? 问题答案: 决定何时启动CMS(为了使此选项生效,您还必须设置)。是确定世代空间大小的选项。 参见例如… http://java.sun.com/docs/hotspot/gc1.4.2/faq.html 通常无

  • 来自MSDN:“当一个对象不可访问时,垃圾收集器会将该对象视为垃圾。然后,当垃圾收集器将一个对象的条目从终结队列移动到freachable队列时,该对象不再被视为垃圾,其内存也不会被回收。此时,垃圾收集器已完成对垃圾的识别。一些被识别为垃圾的对象已被重新分类。”被归类为非垃圾。垃圾收集器压缩可回收内存,特殊运行时线程清空可回收队列,执行每个对象的Finalize方法。需要两个GC来回收需要终结的对