您需要将字段自定义解析为更小、更紧凑的类型。如果您正在存储一串重复的字符串,请使用字符串规范化和重用重复字符串(共享引用)。通常的做法是在解析时使用字符串池来共享引用。
我在这里发布了一个简短的演示项目这里,说明了两种方法
- 使用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包装器libpython-clj,panthera。
关于内存性能的笔记:JVM 默认不会将内存释放回系统,因此它看起来会“增长”到由 -Xmx 设置的限制(默认我想是 2GB),并且它看起来会停滞在那里(即使在垃圾回收周期之后)。如果您想了解实际使用和预留的情况,您需要附加一个分析器并查看堆使用统计信息(如jvisualvm)。