IO与NIO读写文本文件的测试


做日志分析服务中,需要对文本文件作大量的读写和备份操作,对性能要求比较高,于是对IONIO的文件操作类做测试。

IO这里指java“旧”的IO操作。

NIO有过了解的朋友都知道:NIO对文件操作是通过FileInputStreamFileOutputStream或者RandomAccessFile对象的getChannel()方法获得一个FileChannelFileChannelByteBuffer建立联系来完成操作。ByteBuffer是字节缓冲区。顾名思义,ByteBuffer是操作的是缓存在内存中的字节。

在整个测试过程中,发现使用NIOByteBuffer无论是读还是写文件都比“旧”IO性能要高,但是由于NIO操作的是字节,而在我们中国使用最多的是GBK或者UTF-8的字符,这就出现了一个矛盾,把字符转换为字节时的大量消耗,几乎抵消了NIO对于“旧”IO的优势。

也许你会问NIO中不是有CharBuffer,使用NIO的对字符操作呀?很遗憾的告诉你,NIO提供的FileChannel类的readwrite方法中参数都是ByteBuffer,而没有对CharBuffer的支持,这个也可能是个不足的地方。

由于做日志分析服务,对文本文件按行读readLine(),封装字节流和NIO后的形成的readLine()并不比BufferedReadreadLine快。

还有一个比较普遍的说话:读的时候下来了,内存上去了。内存下来了,读的时间上去了。

总结:如果读写是瓶颈,读选择NIO非直接字节缓冲区,写选择NIO的直接字节缓冲区。不过要注意编码。

如果不想考虑太多的问题,使用BufferedReaderBufferedWriter

如果操作的内容全部是英文,那还是选择NIO撒。

温情提示:众所周知,UTF-8是变长编码,一个字符占1-4字节都有可能。

API内容:

BufferedReader :从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

通常,Reader 所作的每个读取请求都会导致对基础字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如,

 BufferedReader in = new BufferedReader(new FileReader("foo.in"));

将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。 

可以对使用 DataInputStream 进行按原文输入的程序进行本地化,方法是用合适的 BufferedReader 替换每个 DataInputStream

-----------------------------------------------------

BufferedWriter :将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。

通常 Writer 将其输出立即发送到基础字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如,

 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));

将缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的

-----------------------------------------------

ByteBuffer:缓冲区

直接 非直接缓冲区

字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。

直接字节缓冲区可以通过调用此类的 allocateDirect 工厂方法来创建。此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们。

直接字节缓冲区还可以通过 mapping 将文件区域直接映射到内存中来创建。Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在访问期间或稍后的某个时间导致抛出不确定的异常。

字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect 方法来确定。提供此方法是为了能够在性能关键型代码中执行显式缓冲区管理。

<!--EndFragment-->

转载请注明出处:http://www.it610.com/article/305371.htm

猜你喜欢

转载自blog.csdn.net/zgmzyr/article/details/49621403