繁体   English   中英

Java GC 如何处理从大 Stream 加载的已处理 object 超过可用堆 ZCD69BZ4957F06CD8181?

[英]How does Java GC deals with processed object loaded from a large Stream exceeding available heap memory?

假设我有一个从数据库加载的对象的 stream(使用 Spring 数据 JPA 如下)

public interface MyJpaRepository extends JpaRepository<Foo, String> {

  Stream<Foo> findAll();
}

假设有数百万个 Foo 对象存储在我的数据库中,使用的 GB 比我的最大堆 memory 大小还要多。

我期望使用 stream 如下将让 JVM 正确处理其堆 memory,因为从数据库加载更多的对象:

try (Stream<Foo> fooStream =
    myJpaRepository.findAll()) {
  fooStream.forEach(entity -> logger.info("Hello !"));
}

但事实上,这个确切的代码抛出了 memory 异常。

  • 垃圾收集器在这种情况下如何工作?
  • How consuming this stream using a forEach requires the JVM to entirely load the data from the stream in memory (as per my understanding)?

谢谢

Java Stream 不会从底层数据库中获取所有数据。 流不存储数据; 相反,它们提供来自集合、数组或 IO 通道等来源的数据。 通常,这些都是惰性评估的。 因此,当每个实体调用looger.info时,stream 将从底层数据存储中获取数据并应用命令。 由于 stream 只提供了一个迭代器,它只需要获取迭代中的下一个数据,而不是整个集合。 一旦应用了 lambda function ,GC 将删除获取的数据。

在您的场景中,垃圾收集器将没有时间来采取行动并清理您的 memory。 让我尝试更详细地解释。 当您启动 java 进程时,您配置了堆 memory 以及垃圾收集算法。 如果您没有对其中任何一个进行微调,JVM 会理所当然地使用默认设置并继续。 一旦您的进程开始分配堆,JVM 在内部收集统计信息并安排垃圾收集进程。 但是,如果您的进程没有提供喘息的空间来决定何时以及如何收集垃圾,JVM 将抛出内存不足(OOM)错误并崩溃,正如您所观察到的。

@ernest_k 在他的评论中是 100%,这个问题与 Streams 无关。 正如@avishek-bhattacharya 解释的那样:

流不存储数据; 相反,它们提供来自集合、数组或 IO 通道等来源的数据。 通常,这些都是惰性评估的。

事实上,Postgres(在我的例子中是底层数据库)总是返回整个 ResultSet,除非另有配置(MySQL 也是如此)。 要将其配置为使用数据库 Cursor,您需要执行以下操作:

public interface MyJpaRepository extends JpaRepository<Foo, String> {

  @QueryHints(
    value = {
      @QueryHint(name = HINT_FETCH_SIZE, value = "1000"),
      @QueryHint(name = HINT_CACHEABLE, value = "false"),
      @QueryHint(name = HINT_READONLY, value = "true")
  })
  Stream<Foo> findAll();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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