直接缓冲区与非直接缓冲区
- 非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在Jvm内存中。
可以观察到非直接缓冲区需要在内核地址空间(OS)和用户地址空间之间(JVM)之间进行数据复制的操作,将缓冲区建立在物理内存中的可以免除中间的复制操作,提高效率因此产生了直接缓冲区。
- 直接缓冲区:通过allocateDirect()工厂方法来创建。此方法的缓冲区进行分配和取消分配所需的成本通常高于非直接缓冲区,直接缓冲区的内容可以驻留在常规的垃圾回收堆之外。
在直接缓冲区中,直接将缓冲区建立在物理内存中,免除了中间的复制操作,但是又多了不安全因素以及消耗内存大等不利因素。
//分配直接内存
ByteBuffer buf = ByteBuffer.allocateDirect(10);
//判断分配的内存是否为直接内存
System.out.println(buf.isDirect());
输出为:true
通道(Channel)
由java.nio.Channels包进行定义,Channel用于连接IO源与目标。Channel类似于传统的“流”,但是不能直接访问数据,Channel只能与Buffer进行交互。
在早期的操作系统,对于应用程序的读写需要经过CPU来进行执行,CPU忙于IO接口之间的读写切换,占用了大量的CPU内存。
在后来,发明了通道,通道专门负责IO接口的读写,将CPU解放出来,极大地提高了CPU的利用率。
Java提供的通道的实现类
本地:
- FileChannel:用于读取、写入、映射和操作文件的通道
网络:
- SocketChannel:通过TCP读写网络中的数据
- ServerSocketChannel:可以监听新进来的TCP连接,对每一个新进来的连接都会创建一个SocketChannel。
- DataGramChannel:通过UDP读写网络中的数据。
获取通道的方法
对支持通道的对象调用getChannel()方法。支持通道的类如下:
- FileInputStream
FileInputStream fis;
try {
fis= new FileInputStream(new File("doc"));
FileChannel fin = fis.getChannel();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- FileoutputStream
- RandomAccessFile
- DatagramSocket
- Socket
ServerSocket
- JDK 1.7中,针对各个通道提供了静态的Open方法。
FileChannel fic;
try {
//获取一个输入通道
fic = FileChannel.open(Paths.get("doc"),StandardOpenOption.READ);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
- 获取字节通道Files的newByteChannel()方法。
try {
ByteChannel dcl=Files.newByteChannel(Paths.get("doc"), StandardOpenOption.READ);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
案例:
- 利用通道完成文件的复制:
public static void copyFile() {
//建立关联文件的输入流
FileInputStream fis = null;
//建立关联文件的输出流
FileOutputStream fos = null;
//建立输入流的通道
FileChannel foc = null;
//建立输出流的通道
FileChannel fic = null;
//获取输入流的通道
try {
fis = new FileInputStream(new File("doc"));
fic = fis.getChannel();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//获取输出流的通道
try {
fos = new FileOutputStream(new File("doc2"));
foc = fos.getChannel();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//建立分配了1024字节的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
try {
while (fic.read(buf) != -1) {
//将缓冲区的模式修改
buf.flip();
foc.write(buf);
buf.clear();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//关闭通道和流
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fic != null) {
try {
fic.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (foc != null) {
try {
foc.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
- 利用直接缓冲区完成文件的复制
public static void copyFileDirect() {
FileChannel fic = null;
FileChannel foc = null;
MappedByteBuffer bif = null;
MappedByteBuffer bof = null;
// 初始化通道
try {
fic = FileChannel.open(Paths.get("doc"), StandardOpenOption.READ);
foc = FileChannel.open(Paths.get("doc2"), StandardOpenOption.WRITE, StandardOpenOption.READ);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 将文件区域直接映射到内存中来创建(内存映射文件)
try {
bif = fic.map(MapMode.READ_ONLY, 0, fic.size());
bof = foc.map(MapMode.READ_WRITE, 0, fic.size());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 直接对缓冲区进行操作
byte[] bytes = new byte[bif.limit()];
bif.get(bytes);
bof.put(bytes);
try {
fic.close();
foc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
- 直接利用通道完成文件的复制
// 通道之间的数据传输
try {
FileChannel fin = FileChannel.open(Paths.get("doc"), StandardOpenOption.READ);
FileChannel fon = FileChannel.open(Paths.get("doc2"), StandardOpenOption.WRITE);
fin.transferTo(0, fin.size(), fon);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
使用的通道的transferTo或者transferFrom方法完成文件的复制。