使用CompletableFuture时内存会累积

时间:2018-01-18 06:48:06

标签: java completable-future

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对象?

关于如何解决这个问题的任何想法?

1 个答案:

答案 0 :(得分:0)

由于您future中存储的DataItemCache,此服务是有状态的。

假设它是一个单例,你构建的CompletableFuture链将在所有调用者之间共享。这意味着如果您并行调用saveItemAsync(),您将构建一个巨大的CompletableFuture链。这可能是你的OOME的原因。

此外,waitForWriteComplete()还会等待其他来电者的期货完成,但可能会遗漏自己的结果 - 因为访问future时没有同步。

最后,由于所有调用都是链接的,因此副作用是一次只处理一个(除了由于访问future时的内存同步问题)。因此,在这里使用CompletableFuture没有多大意义,拥有synchronized同步方法会更简单。这也会消耗更少的内存,并且可能通过避免CompletableFuture的所有开销来提高性能。