来自大型Map的java.lang.OutOfMemoryError,标有@Stringify

时间:2015-01-30 16:34:14

标签: java google-app-engine objectify

如何解决" java.lang.OutOfMemoryError:Java堆空间"对于地图属性timeseries来说,显然太大而无法从Google App Engine数据存储中检索?当我尝试使用Objectify load()操作从数据存储区读取包含大型地图的实体时,会发生错误。

这是包含大型地图的实体:

@com.googlecode.objectify.annotation.Entity
public class Insight {
    @com.googlecode.objectify.annotation.Id 
    public Long id;

    @Stringify(com.netbase.model.DateStringifier.class)
    public Map<Date, Double> timeseries;
}

这是Stringifier实现:

public class DateStringifier implements Stringifier<Date> {

    @Override
    public String toString(Date obj) {
        return Long.toString(obj);
    }

    @Override
    public Date fromString(String str) {
        long timestamp_long = Long.parseLong(timestamp);
        Date date = new Date(timestamp_long);
        return date;
    }    
}

我已经尝试在内存容量最大的Google模块中运行此操作(https://cloud.google.com/appengine/docs/java/modules/中所述的实例类B8)。

我也尝试过使用游标,认为垃圾收集会在光标刷新后发生。但是我实际上在光标大小为1的第一个光标上得到了异常,这表明单个Map本身太大了。

我不确定我是如何能够在一开始就坚持这么大的地图,但它发生在几周前,现在日志丢失了。构建地图是一项昂贵的操作,所以我想看看我是否还能检索它。

由于它与Objectify持续存在,我不确定如何使用基础数据存储区读取它。关于如何解决这个问题的任何建议都将非常受欢迎。

=============================================== =============

更新:我设法通过将其数据导出来从Google App Engine中获取数据。我不确定当原来的问题好像会阻止我时,我是怎么做到的。

无论如何,我想更新你们,让你们知道数据总量是23.5 MB。它有大约6K个实体,每个实体都有Map<Date, Double> timeseries。这意味着每个实体拥有大约3.9 MB的数据。根据@stickfigure在下面的解决方案中,关于实体有多大的限制。因此,我必须找出一些方法将实体分解为较小的实体并在单独的会话中处理数据。我有点失望,因为我认为Google App Engine会给我更多资源。我想,如果我能在Excel文件中保存23.5 MB并且本地计算机上的Excel可以毫无困难地处理这么多数据,那么Google App Engine应该可以处理更多次。

1 个答案:

答案 0 :(得分:1)

此处还有其他事情发生。 GAE实体不能大于1MB;甚至允许多X扩展因子(您的POJO和本地低级实体同时存在于RAM中,字符串形式加上日期形式等)任何单个实体都不能代表超过几兆字节。

你可能正在堆的边缘运行,而这个几兆字节的操作恰好是推动你超越边缘的操作。问题是为什么,特别是如果你已经在使用B8。我会使用分析器在本地运行您的应用程序。

一个好的候选者是在单个会话中通过大量实体进行批量迭代。如果需要,可以在迭代时调用ofy().clear()。您可以在session caching documentation中阅读更多内容。