首页 > 代码库 > kafka.utils.Utils阅读

kafka.utils.Utils阅读

这个类实现了一些工具性质的方法,正如其名。

记下自己觉得有意思的方法:

readFileAsString(path: String, charset: Charset = Charset.defaultCharset()): String

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * Attempt to read a file as a string
 */
def readFileAsString(path: String, charset: Charset = Charset.defaultCharset()): String = {
  val stream = new FileInputStream(new File(path))
  try {
    val fc = stream.getChannel()
    val bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size())
    charset.decode(bb).toString()
  }
  finally {
    stream.close()
  }
}

  这里特殊之处是使用了NIO里FileChannel的内存映射,对目标文件建立内存映射。然后对返回的MappedByteBuffer进行解码, 得到CharBuffer, 然后调用其toString方法获得对应的字符串。

  当处理比较大的文件时,内存映射会带来性能的提升。同时,将整个文件读进一个大的ByteBuffer,然后由这个ByteBuffer进行字符解码,可以直接得到整个文件对应的字符串。同样的功能也可以用FileInputReader的read方法实现。所以,主要考虑还是内存映射。

引用

从代码层面上看,从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并且数据拷贝操作是由文件系统和硬件驱动实现的,理论上来说,拷贝数据的效率是一样的。但是通过内存映射的方法访问硬盘上的文件,效率要比read和write系统调用高,这是为什么呢?原因是read()是系统调用,其中进行了数据拷贝,它首先将文件内容从硬盘拷贝到内核空间的一个缓冲区,如图2中过程1,然后再将这些数据拷贝到用户空间,如图2中过程2,在这个过程中,实际上完成了 两次数据拷贝 ;而mmap()也是系统调用,如前所述,mmap()中没有进行数据拷贝,真正的数据拷贝是在缺页中断处理时进行的,由于mmap()将文件直接映射到用户空间,所以中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了 一次数据拷贝 。因此,内存映射的效率要比read/write效率高。

实际上内存映射就是磁盘的数据会被直接写到用户空间(在内存中);而不用内存映射会先写到内核缓冲,再由CPU拷贝到用户空间,这样就慢了。

Java 中使用内存映射文件需要考虑的 10 个问题