您需要将字段自定义解析为更小、更紧凑的类型。如果您要存储大量重复的字符串,可以使用字符串规范化的方式重用重复的字符串(共享引用)。典型做法是使用字符串池在解析过程中共享引用。
我在这里放了一个演示项目,它以两种方式处理这个问题
- 使用spork.util.table(我旧的数据处理库),它基于持久化结构并使用了我所提到的技术
- 使用tech.ml.dataset,这是利用TableSaw表格实现的一种新的快速、内存高效的结构(可变的,但COW实现以支持持久语义)。
两种解决方案都可以轻松处理300MB的TSV文件,虽然tech.ml.dataset在实践中更节省内存。
我还将一个版本放在测试中,该测试复制了您的测试输入(在某种程度上,我猜测了空值的数量,为1/3)。它展示了使用spork,tech.ml(当前在尝试解析日期时失败)的方式,以及最后使用纯clojure函数手动进行的方式。如果您需要最小的内存占用(以及性能),则解析到原语是必需的。不幸的是,Java集合通常是装箱的(除了数组),因此您需要像FastUtils或另一个基于原语的集合。Clojure基于原语的向量有点帮助,但它们仍然因数组的trie而承担着引用开销(数组只位于叶子)。或者,如果您只是从文件构建一个相同类型的密集数组,可以构造一个原语数组并将其填充。这将节省空间,并且在结构上相容,特别是如果您使用这些浮点数进行数值计算并能利用密集格式。
总之,原始解析,特别是保留对字符串或装箱对象的引用,将需要庞大的堆,因为Java对象很耗内存。正如Andy提到的,新的JVM可以通过压缩字符串(这与字符串规范化的效果相同,但在JVM级别真正很好)在一定程度上减轻这种负担。
注意:这仅适用于您需要“保留”数据的情况......如果您可以遍历它或以其他方式流式计算,则可能可以通过原始解析处理任意数据集(例如超过内存的大小)而不爆堆,因为JVM会在您处理完引用后立即进行垃圾回收。
还有一些库可用于此类任务iota,
以及最近的pandas包装器via libpython-clj,panthera。
关于内存性能的笔记:默认情况下,Java虚拟机(jvm)不会将内存释放回系统,因此它看起来会“增长”到由-Xmx(默认我认为是2GB)设置的极限,并且会看起来保持在那个水平(即使经过垃圾回收周期)。如果您想了解实际使用与保留的内存量,则需要附加一个分析器和查看堆内存使用统计信息(如jvisualvm)。