java.io和java.nio性能简单对比
来源:互联网 发布:为知笔记导入印象笔记 编辑:程序博客网 时间:2024/05/27 00:40
最近我在工作中用到了java i/o相关功能。因为对java.io的了解更多(毕竟面世较早),所以一开始我使用的是java.io包下的类,后来为了测试一下是不是能够通过NIO提高文件操作性能,于是转向了java.nio。我得到的结论让我感到有些震惊,下面是对比测试的一些细节:
1、在java.io的测试代码中,我使用RandomAccessFile直接向文件写数据,并搜索到特定的位置执行记录的插入、读取和删除。
2、在java.nio的初步测试代码中,使用FileChannel对象。NIO之所以比java.io更加高效,是因为NIO面向的是data chunks,而java.io基本上是面向byte的。
3、为了进一步挖掘NIO的能力,我又改用MappedByteBuffer执行测试,这个类是构建在操作系统的虚拟内存机制上的。根据java文档所说,这个类在性能方面是最好的。
为了进行测试,我写了一个模拟员工数据库的小程序,员工数据的结构如下:
员工数据写入文件,并将last name作为索引key,日后可以通过这个key从文件中加载该员工对应的数据。无论使用IO、NIO还是MappedByteBuffers,首先都需要打开一个RandomAccessFile。以下代码在用户的home目录下创建一个名为employee.ejb的文件,设置为可读可写,并初始化对应的Channel和MappedByteBuffer:
使用channel.map进行映射后,当该文件被追加了新的数据时,之前的MappedByteBuffer是看不到这些数据的。因为我们想测试读和写,所以当文件中追加写入新的记录后,需要重新做映射才能使得MappedByteBuffer读取新数据。为了提高效率,降低重新映射的次数,每次空间不够的时候,我们将文件扩张特定的大小(比如说1K)以防止每次追加新记录都要重新映射。
下面是写入员工记录的对比测试:
使用java.io的代码:
使用java.nio的代码:
使用MappedByteBuffer的代码如下:
接下来,调用每种方法插入100,000条记录, 耗时对比如下:
* With java.io: ~10,000 milliseconds
* With java.nio: ~2,000 milliseconds
* With MappedByteBuffer: ~970 milliseconds
使用NIO的性能改善效果非常明显,使用MappedByteBuffer的性能,更是让人吃惊。
使用三种方式读取数据的性能对比如下:
* With java.io: ~6,900 milliseconds
* With java.nio: ~1,400 milliseconds
* With MappedByteBuffer: ~355 milliseconds
和写入的时候情况差不多,NIO有很明显的性能提升,而MappedByteBuffer则有惊人的高效率。从java.io迁移到nio并使用MappedByteBuffer,通常可以获得10倍以上的性能提升。
1、在java.io的测试代码中,我使用RandomAccessFile直接向文件写数据,并搜索到特定的位置执行记录的插入、读取和删除。
2、在java.nio的初步测试代码中,使用FileChannel对象。NIO之所以比java.io更加高效,是因为NIO面向的是data chunks,而java.io基本上是面向byte的。
3、为了进一步挖掘NIO的能力,我又改用MappedByteBuffer执行测试,这个类是构建在操作系统的虚拟内存机制上的。根据java文档所说,这个类在性能方面是最好的。
为了进行测试,我写了一个模拟员工数据库的小程序,员工数据的结构如下:
- class Employee {
- String last; // the key
- String first;
- int id;
- int zip;
- boolean employed;
- String comments;
- }
- class Employee {
- String last; // the key
- String first;
- int id;
- int zip;
- boolean employed;
- String comments;
- }
员工数据写入文件,并将last name作为索引key,日后可以通过这个key从文件中加载该员工对应的数据。无论使用IO、NIO还是MappedByteBuffers,首先都需要打开一个RandomAccessFile。以下代码在用户的home目录下创建一个名为employee.ejb的文件,设置为可读可写,并初始化对应的Channel和MappedByteBuffer:
- String userHome = System.getProperty("user.home");
- StringBuffer pathname = new StringBuffer(userHome);
- pathname.append(File.separator);
- pathname.append("employees.ejb");
- java.io.RandomAccessFile journal =
- new RandomAccessFile(pathname.toString(),"rw");
- //下面这一句是为了NIO
- java.nio.channels.FileChannel channel = journal.getChannel();
- //下面这两句是为了使用MappedByteBuffer
- journal.setLength(PAGE_SIZE);
- MappedByteBuffer mbb =
- channel.map(FileChannel.MapMode.READ_WRITE, 0, journal.length() );
- String userHome = System.getProperty("user.home");
- StringBuffer pathname = new StringBuffer(userHome);
- pathname.append(File.separator);
- pathname.append("employees.ejb");
- java.io.RandomAccessFile journal =
- new RandomAccessFile(pathname.toString(), "rw");
- //下面这一句是为了NIO
- java.nio.channels.FileChannel channel = journal.getChannel();
- //下面这两句是为了使用MappedByteBuffer
- journal.setLength(PAGE_SIZE);
- MappedByteBuffer mbb =
- channel.map(FileChannel.MapMode.READ_WRITE, 0, journal.length() );
使用channel.map进行映射后,当该文件被追加了新的数据时,之前的MappedByteBuffer是看不到这些数据的。因为我们想测试读和写,所以当文件中追加写入新的记录后,需要重新做映射才能使得MappedByteBuffer读取新数据。为了提高效率,降低重新映射的次数,每次空间不够的时候,我们将文件扩张特定的大小(比如说1K)以防止每次追加新记录都要重新映射。
下面是写入员工记录的对比测试:
使用java.io的代码:
- public boolean addRecord_IO(Employee emp) {
- try {
- byte[] last = emp.last.getBytes();
- byte[] first = emp.first.getBytes();
- byte[] comments = emp.comments.getBytes();
- // Just hard-code the sizes for perfomance
- int size = 0;
- size += emp.last.length();
- size += 4; // strlen - Integer
- size += emp.first.length();
- size += 4; // strlen - Integer
- size += 4; // emp.id - Integer
- size += 4; // emp.zip - Integer
- size += 1; // emp.employed - byte
- size += emp.comments.length();
- size += 4; // strlen - Integer
- long offset = getStorageLocation(size);
- //
- // Store the record by key and save the offset
- //
- if ( offset == -1 ) {
- // We need to add to the end of the journal. Seek there
- // now only if we're not already there
- long currentPos = journal.getFilePointer();
- long jounralLen = journal.length();
- if ( jounralLen != currentPos )
- journal.seek(jounralLen);
- offset = jounralLen;
- }else {
- // Seek to the returned insertion point
- journal.seek(offset);
- }
- // Fist write the header
- journal.writeByte(1);
- journal.writeInt(size);
- // Next write the data
- journal.writeInt(last.length);
- journal.write(last);
- journal.writeInt(first.length);
- journal.write(first);
- journal.writeInt(emp.id);
- journal.writeInt(emp.zip);
- if ( emp.employed )
- journal.writeByte(1);
- else
- journal.writeByte(0);
- journal.writeInt(comments.length);
- journal.write(comments);
- // Next, see if we need to append an empty record if we inserted
- // this new record at an empty location
- if ( newEmptyRecordSize != -1 ) {
- // Simply write a header
- journal.writeByte(0);//inactive record
- journal.writeLong(newEmptyRecordSize);
- }
- employeeIdx.put(emp.last, offset);
- return true;
- }
- catch ( Exception e ) {
- e.printStackTrace();
- }
- return false;
- }
- public boolean addRecord_IO(Employee emp) {
- try {
- byte[] last = emp.last.getBytes();
- byte[] first = emp.first.getBytes();
- byte[] comments = emp.comments.getBytes();
- // Just hard-code the sizes for perfomance
- int size = 0;
- size += emp.last.length();
- size += 4; // strlen - Integer
- size += emp.first.length();
- size += 4; // strlen - Integer
- size += 4; // emp.id - Integer
- size += 4; // emp.zip - Integer
- size += 1; // emp.employed - byte
- size += emp.comments.length();
- size += 4; // strlen - Integer
- long offset = getStorageLocation(size);
- //
- // Store the record by key and save the offset
- //
- if ( offset == -1 ) {
- // We need to add to the end of the journal. Seek there
- // now only if we're not already there
- long currentPos = journal.getFilePointer();
- long jounralLen = journal.length();
- if ( jounralLen != currentPos )
- journal.seek(jounralLen);
- offset = jounralLen;
- }else {
- // Seek to the returned insertion point
- journal.seek(offset);
- }
- // Fist write the header
- journal.writeByte(1);
- journal.writeInt(size);
- // Next write the data
- journal.writeInt(last.length);
- journal.write(last);
- journal.writeInt(first.length);
- journal.write(first);
- journal.writeInt(emp.id);
- journal.writeInt(emp.zip);
- if ( emp.employed )
- journal.writeByte(1);
- else
- journal.writeByte(0);
- journal.writeInt(comments.length);
- journal.write(comments);
- // Next, see if we need to append an empty record if we inserted
- // this new record at an empty location
- if ( newEmptyRecordSize != -1 ) {
- // Simply write a header
- journal.writeByte(0); //inactive record
- journal.writeLong(newEmptyRecordSize);
- }
- employeeIdx.put(emp.last, offset);
- return true;
- }
- catch ( Exception e ) {
- e.printStackTrace();
- }
- return false;
- }
使用java.nio的代码:
- public boolean addRecord_NIO(Employee emp) {
- try {
- data.clear();
- byte[] last = emp.last.getBytes();
- byte[] first = emp.first.getBytes();
- byte[] comments = emp.comments.getBytes();
- data.putInt(last.length);
- data.put(last);
- data.putInt(first.length);
- data.put(first);
- data.putInt(emp.id);
- data.putInt(emp.zip);
- byte employed =0;
- if ( emp.employed )
- employed = 1;
- data.put(employed);
- data.putInt(comments.length);
- data.put(comments);
- data.flip();
- int dataLen = data.limit();
- header.clear();
- header.put((byte)1);// 1=active record
- header.putInt(dataLen);
- header.flip();
- long headerLen = header.limit();
- int length = (int)(headerLen + dataLen);
- long offset = getStorageLocation((int)dataLen);
- //
- // Store the record by key and save the offset
- //
- if ( offset == -1 ) {
- // We need to add to the end of the journal. Seek there
- // now only if we're not already there
- long currentPos = channel.position();
- long jounralLen = channel.size();
- if ( jounralLen != currentPos )
- channel.position(jounralLen);
- offset = jounralLen;
- }
- else {
- // Seek to the returned insertion point
- channel.position(offset);
- }
- // Fist write the header
- long written = channel.write(srcs);
- // Next, see if we need to append an empty record if we inserted
- // this new record at an empty location
- if ( newEmptyRecordSize != -1 ) {
- // Simply write a header
- data.clear();
- data.put((byte)0);
- data.putInt(newEmptyRecordSize);
- data.flip();
- channel.write(data);
- }
- employeeIdx.put(emp.last, offset);
- return true;
- }
- catch ( Exception e ) {
- e.printStackTrace();
- }
- return false;
- }
- public boolean addRecord_NIO(Employee emp) {
- try {
- data.clear();
- byte[] last = emp.last.getBytes();
- byte[] first = emp.first.getBytes();
- byte[] comments = emp.comments.getBytes();
- data.putInt(last.length);
- data.put(last);
- data.putInt(first.length);
- data.put(first);
- data.putInt(emp.id);
- data.putInt(emp.zip);
- byte employed = 0;
- if ( emp.employed )
- employed = 1;
- data.put(employed);
- data.putInt(comments.length);
- data.put(comments);
- data.flip();
- int dataLen = data.limit();
- header.clear();
- header.put((byte)1); // 1=active record
- header.putInt(dataLen);
- header.flip();
- long headerLen = header.limit();
- int length = (int)(headerLen + dataLen);
- long offset = getStorageLocation((int)dataLen);
- //
- // Store the record by key and save the offset
- //
- if ( offset == -1 ) {
- // We need to add to the end of the journal. Seek there
- // now only if we're not already there
- long currentPos = channel.position();
- long jounralLen = channel.size();
- if ( jounralLen != currentPos )
- channel.position(jounralLen);
- offset = jounralLen;
- }
- else {
- // Seek to the returned insertion point
- channel.position(offset);
- }
- // Fist write the header
- long written = channel.write(srcs);
- // Next, see if we need to append an empty record if we inserted
- // this new record at an empty location
- if ( newEmptyRecordSize != -1 ) {
- // Simply write a header
- data.clear();
- data.put((byte)0);
- data.putInt(newEmptyRecordSize);
- data.flip();
- channel.write(data);
- }
- employeeIdx.put(emp.last, offset);
- return true;
- }
- catch ( Exception e ) {
- e.printStackTrace();
- }
- return false;
- }
使用MappedByteBuffer的代码如下:
- public boolean addRecord_MBB(Employee emp) {
- try {
- byte[] last = emp.last.getBytes();
- byte[] first = emp.first.getBytes();
- byte[] comments = emp.comments.getBytes();
- int datalen = last.length + first.length + comments.length +12 +9;
- int headerlen =5;
- int length = headerlen + datalen;
- //
- // Store the record by key and save the offset
- //
- long offset = getStorageLocation(datalen);
- if ( offset == -1 ) {
- // We need to add to the end of the journal. Seek there
- // now only if we're not already there
- long currentPos = mbb.position();
- long journalLen = channel.size();
- if ( (currentPos+length) >= journalLen ) {
- //log("GROWING FILE BY ANOTHER PAGE");
- mbb.force();
- journal.setLength(journalLen + PAGE_SIZE);
- channel = journal.getChannel();
- journalLen = channel.size();
- mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, journalLen);
- currentPos = mbb.position();
- }
- if ( currentEnd != currentPos )
- mbb.position(currentEnd);
- offset = currentEnd;//journalLen;
- }
- else {
- // Seek to the returned insertion point
- mbb.position((int)offset);
- }
- // write header
- mbb.put((byte)1);// 1=active record
- mbb.putInt(datalen);
- // write data
- mbb.putInt(last.length);
- mbb.put(last);
- mbb.putInt(first.length);
- mbb.put(first);
- mbb.putInt(emp.id);
- mbb.putInt(emp.zip);
- byte employed =0;
- if ( emp.employed )
- employed = 1;
- mbb.put(employed);
- mbb.putInt(comments.length);
- mbb.put(comments);
- currentEnd += length;
- // Next, see if we need to append an empty record if we inserted
- // this new record at an empty location
- if ( newEmptyRecordSize != -1 ) {
- // Simply write a header
- mbb.put((byte)0);
- mbb.putInt(newEmptyRecordSize);
- currentEnd += 5;
- }
- employeeIdx.put(emp.last, offset);
- return true;
- }
- catch ( Exception e ) {
- e.printStackTrace();
- }
- return false;
- }
- public boolean addRecord_MBB(Employee emp) {
- try {
- byte[] last = emp.last.getBytes();
- byte[] first = emp.first.getBytes();
- byte[] comments = emp.comments.getBytes();
- int datalen = last.length + first.length + comments.length + 12 + 9;
- int headerlen = 5;
- int length = headerlen + datalen;
- //
- // Store the record by key and save the offset
- //
- long offset = getStorageLocation(datalen);
- if ( offset == -1 ) {
- // We need to add to the end of the journal. Seek there
- // now only if we're not already there
- long currentPos = mbb.position();
- long journalLen = channel.size();
- if ( (currentPos+length) >= journalLen ) {
- //log("GROWING FILE BY ANOTHER PAGE");
- mbb.force();
- journal.setLength(journalLen + PAGE_SIZE);
- channel = journal.getChannel();
- journalLen = channel.size();
- mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, journalLen);
- currentPos = mbb.position();
- }
- if ( currentEnd != currentPos )
- mbb.position(currentEnd);
- offset = currentEnd;//journalLen;
- }
- else {
- // Seek to the returned insertion point
- mbb.position((int)offset);
- }
- // write header
- mbb.put((byte)1); // 1=active record
- mbb.putInt(datalen);
- // write data
- mbb.putInt(last.length);
- mbb.put(last);
- mbb.putInt(first.length);
- mbb.put(first);
- mbb.putInt(emp.id);
- mbb.putInt(emp.zip);
- byte employed = 0;
- if ( emp.employed )
- employed = 1;
- mbb.put(employed);
- mbb.putInt(comments.length);
- mbb.put(comments);
- currentEnd += length;
- // Next, see if we need to append an empty record if we inserted
- // this new record at an empty location
- if ( newEmptyRecordSize != -1 ) {
- // Simply write a header
- mbb.put((byte)0);
- mbb.putInt(newEmptyRecordSize);
- currentEnd += 5;
- }
- employeeIdx.put(emp.last, offset);
- return true;
- }
- catch ( Exception e ) {
- e.printStackTrace();
- }
- return false;
- }
接下来,调用每种方法插入100,000条记录, 耗时对比如下:
* With java.io: ~10,000 milliseconds
* With java.nio: ~2,000 milliseconds
* With MappedByteBuffer: ~970 milliseconds
使用NIO的性能改善效果非常明显,使用MappedByteBuffer的性能,更是让人吃惊。
使用三种方式读取数据的性能对比如下:
* With java.io: ~6,900 milliseconds
* With java.nio: ~1,400 milliseconds
* With MappedByteBuffer: ~355 milliseconds
和写入的时候情况差不多,NIO有很明显的性能提升,而MappedByteBuffer则有惊人的高效率。从java.io迁移到nio并使用MappedByteBuffer,通常可以获得10倍以上的性能提升。
- 翻译+转:java.io和java.nio性能简单对比
- 翻译+转:java.io和java.nio性能简单对比
- java.io和java.nio性能简单对比
- java.io和java.nio性能简单对比
- Java NIO 和IO 对比
- java对比IO和NIO的文件读写性能测试
- 对比Java.nio 和 Java.io
- 对比Java.nio 和 Java.io
- 对比Java.nio 和 Java.io
- 对比Java.nio 和 Java.io
- 对比Java.nio 和 Java.io
- java IO和NIO性能比较
- Java中的NIO和IO的对比分析
- Java中的NIO和IO的对比分析
- java io和nio
- java IO和NIO
- Java IO和NIO
- Java NIO和IO
- hdu 1708
- 滑动页面
- spring+hibernate 二级缓存 配置+java使用实例
- Eclipse 报错 Cannot nest 'FisRptWeb/src/conf' inside library 'FisRptWeb/src'
- asp.net获取Repeater里面Button提交后的文本框的值
- java.io和java.nio性能简单对比
- 动态库与静态库的异同、生成和使用详解
- 别瞎忙活:创业公司的6条时间管理策略
- SQL Server 2008中新增的变更数据捕获(CDC)和更改跟踪
- iPhone/Mac Objective-C内存管理教程和原理剖析
- 子进程从父进程得到了什么
- USIM 相关知识,术语
- POJ 1789 Truck History
- 2012CSDN博客之星求支持