java.lang.OutOfMemoryError 来自标有 @Stringify 的大地图
java.lang.OutOfMemoryError from large Map marked with @Stringify
我如何解决地图 属性 timeseries
的 "java.lang.OutOfMemoryError: Java heap space" 显然太大而无法从 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 模块中尝试 运行 这个(实例 Class B8,如 https://cloud.google.com/appengine/docs/java/modules/ 所述)。
我也尝试过使用游标,认为垃圾回收会在游标刷新后发生。但实际上我在游标大小为 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 应该能够处理很多次更多
这里还有其他事情发生。 GAE 实体不能大于 1MB;即使允许多个 X 扩展因子(您的 POJO 和本机低级实体同时存在于 RAM 中,字符串形式加日期形式等)任何单个实体都不能表示超过几兆字节。
您可能 运行 处于堆的边缘,而这个几兆字节的操作恰好是将您推到边缘的操作。问题是为什么,特别是如果您已经在使用 B8。我会 运行 使用分析器在本地使用您的应用程序。
一个好的候选者是在单个会话中对大量实体进行批量迭代。如果需要,可以在迭代时调用 ofy().clear()
。您可以在 session caching documentation.
中阅读更多内容
我如何解决地图 属性 timeseries
的 "java.lang.OutOfMemoryError: Java heap space" 显然太大而无法从 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 模块中尝试 运行 这个(实例 Class B8,如 https://cloud.google.com/appengine/docs/java/modules/ 所述)。
我也尝试过使用游标,认为垃圾回收会在游标刷新后发生。但实际上我在游标大小为 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 应该能够处理很多次更多
这里还有其他事情发生。 GAE 实体不能大于 1MB;即使允许多个 X 扩展因子(您的 POJO 和本机低级实体同时存在于 RAM 中,字符串形式加日期形式等)任何单个实体都不能表示超过几兆字节。
您可能 运行 处于堆的边缘,而这个几兆字节的操作恰好是将您推到边缘的操作。问题是为什么,特别是如果您已经在使用 B8。我会 运行 使用分析器在本地使用您的应用程序。
一个好的候选者是在单个会话中对大量实体进行批量迭代。如果需要,可以在迭代时调用 ofy().clear()
。您可以在 session caching documentation.