class DataItemCache {
private CompletableFuture future;
public DataItemCache() {
future = CompletableFuture.completedFuture(null);
}
public void saveItemAsync(Object dataItem) {
future = future.thenRunAsync(() -> {
saveItemSync(dataItem); // Saves the item to Elastic Search
});
}
public void waitForWriteComplete() {
future.get();
}
此类的使用方法如下:
class DataProcessorIntegrationTest {
@Inject private DataItemCache dataItemCache;
@Before
public void setup() {
// Setup Guice for injection
}
@Test
public void testWorkflow() {
int numItems = 1000;
for (int index = 0; index < numItems; index++) {
DataItem obj = ... // build data item
dataItemCache.saveItemAsync(obj);
}
// I have code to periodically dump the heap during this wait
dataItemCache.waitForWriteComplete();
// assert that Elastic Search has 1000 items
}
}
在waitForWriteComplete()返回之前分配的DataItem数量为1000,之后立即为0.我期望DataItems的数量将小于1000,因为的一些已完成写入弹性搜索(我从控制台日志中知道)。
当我运行此代码是生产时,我得到了OOM。在OOM时检查堆显示了数百万个DataItem和CompletableFuture对象。显然,他们中的许多人必须完成写作并从saveItem方法返回。为什么没有释放这样的DataItem和CompletableFuture对象?
关于如何解决这个问题的任何想法?
答案 0 :(得分:0)
由于您future
中存储的DataItemCache
,此服务是有状态的。
假设它是一个单例,你构建的CompletableFuture
链将在所有调用者之间共享。这意味着如果您并行调用saveItemAsync()
,您将构建一个巨大的CompletableFuture
链。这可能是你的OOME的原因。
此外,waitForWriteComplete()
还会等待其他来电者的期货完成,但可能会遗漏自己的结果 - 因为访问future
时没有同步。
最后,由于所有调用都是链接的,因此副作用是一次只处理一个(除了由于访问future
时的内存同步问题)。因此,在这里使用CompletableFuture
没有多大意义,拥有synchronized
同步方法会更简单。这也会消耗更少的内存,并且可能通过避免CompletableFuture
的所有开销来提高性能。