从Java中的大文件中读取特定行

时间:2014-08-20 08:54:02

标签: java performance file-io

我正在进行文件处理练习,我需要在给定行号的大文本文件中读取特定行(仅包含ASCII字符的多GB文件)。

到目前为止我做了什么:

由于文件没有相同大小的行,我处理文件以创建对应于行号(冷启动)的每行的偏移的Hashmap。随后,该偏移用于寻找随机访问文件(给定的文本文件)。这种方法的问题在于,对于非常大的文件大小,内存不足以容纳Hashmap,而且I / O成为瓶颈。我的系统规格是:8GB DDR2,1TB(SATA2)7200RPM,4-64位内核。我们假设整个硬件都在我手中。

我打算做什么

为了最大限度地减少延迟,我打算将文本文件中的所有行索引到页面中,并使用最近最少使用的页面替换策略将必要的页面分页到内存中,具体取决于包含所请求行的页面数字是否存在于内存中。 (就像缓存一样) 据我所知,很大程度上取决于页面大小和内存中常驻页面的数量,但我真的需要知道我是否朝着更有意义的方向前进,或者它是否只是一个过度杀伤?

感谢您的所有帮助和建议。

1 个答案:

答案 0 :(得分:3)

  

每行偏移的Hashmap

这就是问题所在。地图可能很容易占用比文件本身更多的内存。

存储每个例如的偏移量第1024行需要1024倍的内存。从磁盘读取一行或一千行需要大约相同的时间,因为:

  • 最小的可访问单元是一个块(以前是512 B,但现在是4KiB)
  • 与寻道时间相比,块的传输时间可以忽略不计

因此向下舍入到1024的倍数,找到该行并按顺序处理到您真正想要的行。


您正在使用HashMap<Integer, Integer>,这会为盒装int占用大量内存。但是你的密钥会构建一个序列,那么为什么不使用List<Integer>甚至是int[]?您可以将内存消耗减少4倍。

我按照以下方式访问List<int[]> offsets

int getOffset(int line) {
    int listIndex = line >> 20; // one list entry per 1M lines
    int arrayIndex = (line >> 10) & 1023; one array entry per 1k lines
    int remaining = line & 1023; // lines to skip

    // TODO: handling of index bounds
    int offset = offsets.get(listIndex)[arrayIndex];
    **seek to offset
    **skip remaining lines
}

每1024行需要4个字节(int),每1MiB需要更多内存。除非您的文本文件是多字节的,否则您不需要LRU,因为一切都很合适。

对于大文件,请在上面的文字中将int替换为long