java 文件io
java 的 io 主要有文件io,网络 io
文件 io 可以分为以下3种:
- 普通 io 就是 java.io 包里 的所有类和接口;
- 基于 FileChannel(文件通道) 的io;
- 基于MMAP(内存映射)的io
普通 IO
概念
IO流用来处理设备之间的数据传输,Java对数据的传输是通过流的方式,Java用于操作流的类都在IO包中。
流按流向分为两种:
- 输出流(读取数据)和输入流(写数据)
流按操作类型分为两种:
- 字节流:字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
- 字符流:字符流只能操作纯字符数据,比较方便
常用的IO流
标准IO
字节流
文件
FileInputStream
FileOutputStream
数组
ByteArrayInputStream
ByteArrayOutputSream
基本数据类型
DataInputStream
DataOuptputStream
缓冲
BufferedInputStream
BufferedOutputStream
对象序列化反序列化
ObjectInputStream
ObjectOutputStream
字符流
字节流转字符流
InputStreamReader
OutputStreamWriter
文件
FileReader
FileWriter
数组
CharArrayReader
CharArrayWriter
缓冲
BufferedReader
BufferedWriter
装饰器模式
示例
public static void main(String[] args) throws IOException {
/**
* 1.每次只读一个字节,又觉得太慢
* 2.一次性读一个文件的所有数据,又怕内存装不下
* 内存只有2G,视频3G,这样就会内存溢出
* 3.最终解决方法:折中,定义每次读8KB
* 每次读多少和:文件系统块大小,CPU缓存大小和缓存延迟 有关系,一般设置为4kb或者8kb
*/
//1.输入流
FileInputStream fis = new FileInputStream("C:/Desktop/a/cm.mp4");
//2.输出流
FileOutputStream fos = new FileOutputStream("C:/Desktop/a/cm-副本2.mp4");
//3.定义个8kb字节数组,也叫缓冲区流
byte[] bytes = new byte[1024 * 8];
int len = 0;
int i = 0;
while( (len = fis.read(bytes)) != -1){
i++;
//4.写入文件
fos.write(bytes,0,len);
}
System.out.println("读取的次数:" + i);
//5.关闭流
fis.close();
fos.close();
}
传统io过程
标准I/O处理,完成一次完整的数据读写,至少需要从底层硬件读到内核空间,再读到用户文件,又从用户空间写入内核空间,再写入底层硬件
- 在传统的文件IO操作中,都是调用操作系统提供的底层标准IO系统调用函数 read()、write(),此时调用此函数的进程(在JAVA中即java进程)由当前的用户态切换到内核态
- 然后OS的内核代码负责将相应的文件数据读取到内核的IO缓冲区,
- 然后再把数据从内核IO缓冲区拷贝到进程的私有地址空间中去,这样便完成了一次IO操作
底层通过write、read等函数进行I/O系统调用时,需要传入数据所在缓冲区起始地址和长度由于JVM GC的存在,导致对象在堆中的位置往往会发生移动,移动后传入系统函数的地址参数就不是真正的缓冲区地址了,可能导致读写出错,为了解决上面的问题,使用标准I/O进行系统调用时,还会额外导致一次数据拷贝:把数据从JVM的堆内拷贝到堆外的连续空间内存(堆外内存)
所以总共经历6次数据拷贝,执行效率较低
-
DMA
全称叫直接内存存取(Direct Memory Access),是一种允许外围设备(硬件子系统)直接访问系统主内存的机制。基于 DMA 访问方式,系统主内存与硬件设备的数据传输可以省去CPU 的全程调度
-
page cache
因为硬盘和内存的读写性能差距巨大,Linux默认情况是以异步方式读写文件的。比如调用系统函数open()打开或者创建文件时缺省情况下是带有O_ASYNC flag的。Linux借助于内核的page cache来实现这种异步操作。
也就是说,我们平常向硬盘写文件时,默认异步情况下,并不是直接把文件内容写入到硬盘中才返回的,而是成功拷贝到内核的page cache后就直接返回,所以大多数情况下,硬盘写操作不会是性能瓶颈。写入到内核page cache的pages成为dirty pages,稍后会由内核线程pdflush真正写入到硬盘上。
从硬盘读取文件时,同样不是直接把硬盘上文件内容读取到用户态内存,而是先拷贝到内核的page cache,然后再“拷贝”到用户态内存,这样用户就可以访问该文件。因为涉及到硬盘操作,所以第一次读取一个文件时,不会有性能提升;不过,如果一个文件已经存在page cache中,再次读取该文件时就可以直接从page cache中命中读取不涉及硬盘操作,这时性能就会有很大提高。
下面用dd比较下异步(缺省模式)和同步写硬盘的速度差别:
# dd if=/dev/urandom of=async.txt bs=64M count=16 iflag=fullblock
16+0 records in
16+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 7.618 s, 141 MB/s
$ dd if=/dev/urandom of=sync.txt bs=64M count=16 iflag=fullblock oflag=sync
16+0 records in
16+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 13.2175 s, 81.2 MB/s