您需要将字段自定义解析为更小、更紧凑的格式。如果您存储一堆重复的字符串,请使用字符串规范化和重用重复字符串(共享引用)。通常的做法是使用字符串池在解析时共享引用。
我在此处发布了一个简短的演示项目此处,按两种方式处理这个任务
- 使用spork.util.table(我的旧表格处理库),它基于持久结构并使用我提到的技术
- 使用tech.ml.dataset,这是最近的一个新努力,利用TableSaw表格实现以快速、内存高效的结构(可变但COW实现以持久语义)。
这两个方案都可以轻松处理300mb的tsv文件(默认堆约2gb),尽管在实际应用中tech.ml.dataset的内存效率更高。
我还将一个版本放入此处,以复制您的测试输入(在缺乏信息的情况下,我在1/3处猜测),展示了使用spork、tech.ml(目前尝试解析日期时失败)和最后使用纯clojure函数手动执行的方法。如果您想要最小的内存占用量(和性能),则解析到原始数据类型是必需的。遗憾的是,Java集合通常是装箱的(除数组外),因此您需要一个像FastUtils或其他原始集合。Clojure原始向量有点帮助,但它们仍然因为数组(数组只位于叶子)的trie而产生参考开销。或者,如果您只需从文件构建一个类型同质的密集数组,您可以构造一个原始数组并用它填充。这将节省空间且满足机械性,如果您使用这些浮点数进行数值计算并且可以利用密集格式。
简而言之,- 朴素解析,尤其是当保留字符串或装箱对象引用时 - 将需要大量的堆,因为Java对象对内存的需求很大。正如Andy提到的那样,新版本的JVM可以通过压缩字符串(这实际上与字符串规范化做同样的事情,但在JVM级别上效果非常好)略微缓解这个问题。
注意:这仅适用于您必须“保留”数据的情况下......如果您可以对此进行迭代计算或进行其他流式计算,您可能可以绕过原始解析来自处理任意大小的数据集(例如,大于内存的大小)而不会耗尽堆,因为JVM会在您处理完毕后立即回收旧引用。
对于这类任务还有一些有用的库iota,
以及最近通过libpython-clj包装的pandaspanthera。
关于内存性能的说明:默认情况下,JVM不会将内存释放回系统,因此它将看起来在直到由-Xmx设置的限制(我认为默认是2GB)时“增长”,并且它将保持在那里(甚至在垃圾回收周期之后)。如果您想了解实际使用与保留的内存比例,则需要附加一个分析器并查看堆使用统计(如jvisualvm)。