【发布时间】:2021-05-21 00:59:02
【问题描述】:
假设我有一个从数据库加载的对象流(如下使用 Spring Data JPA)
public interface MyJpaRepository extends JpaRepository<Foo, String> {
Stream<Foo> findAll();
}
假设有数百万个 Foo 对象存储在我的数据库中,使用的 GB 远远超过我的最大堆内存大小。
我希望按照以下方式使用流将让 JVM 通过垃圾收集处理过的对象来正确处理其堆内存,因为从数据库中加载了更多对象:
try (Stream<Foo> fooStream =
myJpaRepository.findAll()) {
fooStream.forEach(entity -> logger.info("Hello !"));
}
但事实上,这段代码会引发内存不足异常。
- 垃圾收集器在这种情况下如何工作?
- 如何使用 forEach 使用此流需要 JVM 将数据从流中完全加载到内存中(根据我的理解)?
谢谢
【问题讨论】:
-
这与垃圾收集器无关。这是关于您将那么多数据加载到内存中并使其不符合垃圾收集的条件。您的
findAll()方法应该返回一个流,该流根据需要从数据库中获取数据(就像读取结果集时所做的那样)。 -
@ernest_k 你说得对!刚刚了解到 Postgres(在我的例子中是底层数据库)总是返回整个 ResultSet,除非另有配置。正如您所指出的,问题不在于我的
findAll()method 的流代码。即使我觉得这个问题很愚蠢,我该如何配置它以按需获取数据?如果我正在考虑为HINT_FETCH_SIZE添加QueryHint,我的方向是否正确?你能用一个可行的例子回答我的问题吗? -
问题在于
findAll()的实现。我怀疑它是由 spring-data-jpa 给出的。该实现应该是提供不是来自已加载到内存中的数据的流的实现。例如,一个简单的实现会将数据加载到一个列表中并在上面调用.stream()。刚看了Stream rows from PostgreSQL (with fetch size),发现可能是问题所在。我对Spring不熟悉,但也许有答案。 -
这可能是库失败的情况,您需要从中获取一些低级工件,然后您自己在代码中从那里获取它(例如,要求它给您结果设置并使用迭代技术从它构建您的流)。但是这个建议应该来自熟悉 spring-data-jpa 的人。
标签: java java-8 spring-data-jpa java-stream