[英]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 异常。
谢谢
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.