Java 压缩20M文件从30秒到1秒的优化过程,还不相信?( 二 )


在NIO中能够产生的有三个类 。分别是、、以及既能读又能写的 。
源码如下
public static void zipFileChannel() {//开始时间long beginTime = System.currentTimeMillis();File zipFile = new File(ZIP_FILE);try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));WritableByteChannel writableByteChannel = Channels.newChannel(zipOut)) {for (int i = 0; i < 10; i++) {try (FileChannel fileChannel = new FileInputStream(JPG_FILE).getChannel()) {zipOut.putNextEntry(new ZipEntry(i + SUFFIX_FILE));fileChannel.transferTo(0, FILE_SIZE, writableByteChannel);}}printInfo(beginTime);} catch (Exception e) {e.printStackTrace();}}
我们可以看到这里并没有使用进行数据传输 , 而是使用了的方法 。这个方法是将两个通道进行直连 。
This method is potentially much more efficient than a simple loop* that reads from this channel and writes to the target channel.Many* operating systems can transfer bytes directly from the filesystem cache* to the target channel without actually copying them.
这是源码上的描述文字 , 大概意思就是使用的效率比循环一个读取出来然后再循环写入另一个好 。操作系统能够直接传输字节从文件系统缓存到目标的中 , 而不需要实际的copy阶段 。
copy阶段就是从内核空间转到用户空间的一个过程
可以看到速度相比较使用缓冲区已经有了一些的提高 。
------ChannelfileSize:20Mconsum time:1416
内核空间和用户空间
那么为什么从内核空间转向用户空间这段过程会慢呢?首先我们需了解的是什么是内核空间和用户空间 。在常用的操作系统中为了保护系统中的核心资源 , 于是将系统设计为四个区域 , 越往里权限越大 , 所以Ring0被称之为内核空间 , 用来访问一些关键性的资源 。Ring3被称之为用户空间 。
image
用户态、内核态:线程处于内核空间称之为内核态 , 线程处于用户空间属于用户态
那么我们如果此时应用程序(应用程序是都属于用户态的)需要访问核心资源怎么办呢?那就需要调用内核中所暴露出的接口用以调用 , 称之为系统调用。例如此时我们应用程序需要访问磁盘上的文件 。此时应用程序就会调用系统调用的接口open方法 , 然后内核去访问磁盘中的文件 , 将文件内容返回给应用程序 。大致的流程如下
image

Java 压缩20M文件从30秒到1秒的优化过程,还不相信?

文章插图
直接缓冲区和非直接缓冲区
既然我们要读取一个磁盘的文件 , 要废这么大的周折 。有没有什么简单的方法能够使我们的应用直接操作磁盘文件 , 不需要内核进行中转呢?有 , 那就是建立直接缓冲区了 。
既然直接缓冲区那么快 , 我们为什么不都用直接缓冲区呢?其实直接缓冲区有以下的缺点 。直接缓冲区的缺点:
不安全
消耗更多 , 因为它不是在JVM中直接开辟空间 。这部分内存的回收只能依赖于垃圾回收机制 , 垃圾什么时候回收不受我们控制 。
数据写入物理内存缓冲区中 , 程序就丧失了对这些数据的管理 , 即什么时候这些数据被最终写入从磁盘只能由操作系统来决定 , 应用程序无法再干涉 。
综上所述 , 所以我们使用方法就是直接开辟了一段直接缓冲区 。所以性能相比而言提高了许多
使用内存映射文件
NIO中新出的另一个特性就是内存映射文件 , 内存映射文件为什么速度快呢?其实原因和上面所讲的一样 , 也是在内存中开辟了一段直接缓冲区 。与数据直接作交互 。源码如下