您需要将字段自定义解析为更小的、更紧凑的类型。如果您存储了许多重复的字符串,可以使用字符串规范化的方法来重用重复字符串(共享引用)。实现这一点的典型方法是在解析时使用字符串池来共享引用。
我在这里发布了一个简单的演示项目这里,介绍了两种做法
- 使用 spork.util.table(我旧的表格处理库),该库基于持久结构并使用我提到的技术
- 使用 tech.ml.dataset,这是一种利用TableSaw表格实现的新技术,用于快速、内存高效的 结构(可变的,但COW实现用于持久语义)。
这两种解决方案都可以很好地处理约为300MB的tsv文件,尽管默认堆(大约2GB),但在实际应用中tech.ml.dataset的记忆效率要显著更高。
我还将一个版本放入了测试中,以重复您的测试输入(在某种程度上,我猜测了多少空值,为1/3)。它展示了使用spork、tech.ml(目前解析日期时失败),以及最后使用纯Clojure函数手动处理的方法。如果您想要最小的内存足迹(以及性能),则解析为原语是必需的。不幸的是,Java 集合通常是装箱的(除了数组之外),所以您需要像FastUtils或另一个原始集合。Clojure原始向量有点帮助,但它们仍然因数组_certificate的引用而产生开销(数组只出现在叶子上)。或者,如果您只是从文件中构建一个同质类型的密集数组,您可以构建一个原始数组并填充它。这将非常节省空间且与机械性能相容,尤其是如果您正在使用这些浮点数进行数字处理并且可以利用密集格式。
底线是,- 原始解析,尤其是当保留对字符串或装箱对象的引用时 - 由于Java对象内存消耗大,将需要巨大的堆。正如Andy所提到的,较新的JVM可以通过压缩字符串(它有效地执行与字符串规范化相同的事情,但在JVM级别,真的很好)来稍微缓解这个问题。
注意:这仅适用于您必须“保留”数据的情况下……如果您可以对其进行折叠或以其他方式进行流计算,则可能可以在不影响堆的情况下使用原始解析处理任意数据集(例如,大于内存),因为JVM会在您处理完毕后立即回收旧引用。
还有一些其他有用的库可用于此类任务 iota,
以及通过libpython-clj的最新pandas包装器panthera。
关于内存性能的说明:默认情况下,JVM不会将内存释放回系统,因此它将“增长”到由-Xmx设置的极限(我认为默认是2GB),并且它似乎会保持在那个位置(即使经过垃圾回收周期)。如果您想了解实际使用与保留的情况,您需要附加一个分析器并检查堆使用统计信息(如jvisualvm)。