从Hibernate查询加载行时,我一直收到java.lang.OutOfMemoryError: GC overhead limit exceeded
。
我已经尝试过多次增加内存,而且还会继续发生。我在日志中注意到它似乎指向了我使用TreeMap
的方法。我想知道我是否错误地使用了这个导致内存不足的问题。
public List<Item> getProducts() {
List<ProductListing> productListings = session.createCriteria(ProductListing.class)
.createAlias("productConfiguration", "productConfiguration")
.add(Restrictions.eq("productConfiguration.category", category))
.add(Restrictions.eq("active", true))
.add(Restrictions.eq("purchased", true)).list();
Map<String, Item> items = new TreeMap<>();
productListings.stream().forEach((productListing) -> {
Item item = productListing.getItem();
items.put(item.getName(), item);
});
return new ArrayList<>(items.values());
}
将值传递给arraylist是否安全?
我需要设置arraylist大小吗?
我只是想知道我是否做了一件非常糟糕的事情。它看起来是正确的,但内存异常另有说法。
答案 0 :(得分:3)
我将此称为“加载世界” - 名称getProducts()
(没有args)有点代码味道 - 因为您没有限制结果集大小,并且我们知道您的所有{ {1}}对象可能很大,有很多急切加载的依赖项(更不用说堆上的所有后备Hibernate对象)。
另一个大问题是,您将脱水实体昂贵地添加到Item
,调用TreeMap
和可能hashCode()
,只是丢弃密钥并将值复制到新分配的equals()
。
暂且不考虑ArrayList
的预先规模(正确,这不理想,虽然它应该只是缓慢),为什么ArrayList
阶段?如果您需要进行聚合,为什么不让数据库更有效地执行此操作(例如通过TreeMap
)并使用索引,而不是将其全部拉入地图以进行更慢的重新处理?至少,通过只返回唯一的GROUP BY name
,您可以跳过地图阶段并直接复制到您的列表中(根据您的需要,甚至还有更轻量级的可能性)。
我强烈建议使用合适的Profiler进行测试。具体来说,它可以帮助您确定预先加载对堆大小的影响。可能只是将其关闭可能会使问题更易于管理。
但是,您还需要考虑代码的客户:谁真正需要那些Item
的全部?很可能没有人。