io with java
Table of Contents
目的
本文目的对本人对io的理解纠正
filechannel
BIO,NIO貌似NIO就是非阻塞,那么看如下代码:
RandomAccessFile file = new RandomAccessFile("/home/paul/opensource/yutak-dev/demo.txt","rw");
FileChannel channel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
buffer.clear();
channel.read(buffer);
buffer.flip();
byte[] array = new byte[buffer.limit()];
buffer.get(array);
char[] chars = new char[array.length];
for (int i = 0; i < array.length; i++) {
chars[i] = (char) array[i];
}
System.out.println(String.valueOf(chars));
这是我的第一个误区,就是NIO不应该不阻塞吗?
实际上:
这里看了不少的blog,概念很多,我认为还是根据场景分析吧,记忆概念提升不高
IO对于调用者来说,阻塞就是调用代码时,需要立即获得结果,同步执行,我们经常作为client使用,比如等待sql,等待http response.etc就是这种同步使用,非阻塞的话实际上要使用异步api,CompletableFuture很经典,当然其存在whenCompleteAsync的api,实际上就是非当前thread执行回调逻辑,下面这段code执行结果:world hello,其实client这边考虑的很简单,要么一直等待,要么通过事件驱动,挂载一个Handler,数据到达后通知
- 异步阻塞
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 13;
}).whenComplete((a,t)->{
System.out.println(a);
});
TimeUnit.SECONDS.sleep(5);
- 异步非阻塞
CompletableFuture.supplyAsync(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 13;
}).whenComplete((a,t)->{
System.out.println(a);
});
TimeUnit.SECONDS.sleep(10);
MappedByteBuffer
api角度上是针对fileChannel的map函数
RandomAccessFile file = new RandomAccessFile("/home/paul/opensource/yutak-dev/demo.txt", "rw");
MappedByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, file.length());
for (int i = 0; i < 1024 * 1024 * 1024; i += 4*1024) {
buffer.get(i);
}
fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB);,进行映射是一个消耗极少的ops,此时并不意味着 1G 的文件被读进了pageCache
mmap映射的过程可以理解为一个lazy load,只有get()时才会触发缺页中断,DMAload进OS- 预读大小是由
OS算法决定的,可以默认4kb,所以说如果想在这个实验中加载内存到pageCache实际上是要不断get() mmap的生命周期可以视为:map(映射),get/load (缺页中断),clean(回收),很多文章将mmap的提升归功于减少cpu拷贝,实际上我认为是用户态trap进入内核态的消除,最常见的情况就是鼠标的轮询率,高轮询率的时候,系统占用陡增,并且可能出现卡顿,然而如果说,对buffer的数据read(),虽然无需换态,但是依然需要从page cache读数据进入userCache,总结下,就是一段自定义的堆外内存
public static void main(String[] args) throws Exception {
long s = System.currentTimeMillis();
do3();
System.out.println("cost time mills:"+ (System.currentTimeMillis() - s));
}
public static void do1() throws Exception {
RandomAccessFile file = new RandomAccessFile("/home/paul/opensource/yutak-dev/demo.txt", "rw");
MappedByteBuffer buffer = file.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, file.length());
for (int i = 0; i < 1024 * 1024 * 1024; i ++) {
buffer.get(0);
}
}
public static void do3() throws Exception {
RandomAccessFile file = new RandomAccessFile("/home/paul/opensource/yutak-dev/demo.txt", "rw");
FileChannel channel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1);
for (int i = 0; i < 1024 * 1024 * 1024 ; i ++) {
channel.read(buffer);
}
}
这两段逻辑,如果调用次数较少,实际上是不分伯仲的,但是将次数调大,这个数据1GB,filechannel几乎停滞,本实验并不严谨,但是在模拟的情况下,二者已经跨越多个数量级,足以保证实验的基本正确性,,所以说,高并发的时候,换态性能损耗最大
file system
给出一张linux的图片供以后温习
