java的内存映射io为什么快 内存映射是怎么回事
大家好,关于java的内存映射io为什么快很多朋友都还不太明白,今天小编就来为大家分享关于内存映射是怎么回事的知识,希望对各位有所帮助!
内存映射是怎么回事
主要是只硬盘上文件的位置与进程逻辑地址空间中一块大小相同的区域之间的一一对应,如图1中过程1所示。这种对应关系纯属是逻辑上的概念,物理上是不存在的,原因是进程的逻辑地址空间本身就是不存在的。在内存映射的过程中,并没有实际的数据拷贝,文件没有被载入内存,只是逻辑上被放入了内存,具体到代码,就是建立并初始化了相关的数据结构(struct address_space),这个过程有系统调用mmap()实现,所以建立内存映射的效率很高。建立内存映射并没有实际拷贝数据,这时,MMU在地址映射表中是无法找到与ptr相对应的物理地址的,也就是MMU失败,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则会通过mmap()建立的映射关系,从硬盘上将文件读取到物理内存中这个过程与内存映射无关。
如果在拷贝数据时,发现物理内存不够用,则会通过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘上,这个过程也与内存映射无关。
java mmap
java mmap是什么,让我们一起了解一下?
mmap是将一个文件或者其它对象映射进内存,文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。mmap在用户空间映射调用系统中作用很大。
目前Java提供的mmap只有内存文件映射,其他IO操作还没有内存映射功能。
Java内存映射文件(Memory Mapped Files)就已经在java.nio包中,但它对很多程序开发者来说仍然是一个相当新的概念。引入NIO后,Java IO已经相当快,而且内存映射文件提供了Java有可能达到的最快IO操作,这也是为什么那些高性能Java应用应该使用内存映射文件来持久化数据。
mmap在Java中的用途是什么?
1、对普通文件使用mmap提供内存映射I/O,以避免系统调用(read、write、lseek)带来的性能开销。同时减少了数据在内核缓冲区和进程地址空间的拷贝次数。
2、使用特殊文件提供匿名内存映射。
3、使用shm_open以提供无亲缘关系进程间的Posix共享内存区。
mmap在Java中是如何使用的?(具体参考kafka源码中的OffsetIndex这个类)
操作文件,就相当于操作一个ByteBuffer一样。 public class TestMmap {undefined public static String path = "C:\\Users\\64371\\Desktop\\mmap"; public static void main(String[] args) throws IOException {undefined File file1 = new File(path, "1"); RandomAccessFile randomAccessFile = new RandomAccessFile(file1, "rw"); int len = 2048;// 映射为2kb,那么生成的文件也是2kb MappedByteBuffer mmap = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, len); System.out.println(mmap.isReadOnly()); System.out.println(mmap.position()); System.out.println(mmap.limit());// 写数据之后,JVM 退出之后会强制刷新的 mmap.put("a".getBytes()); mmap.put("b".getBytes()); mmap.put("c".getBytes()); mmap.put("d".getBytes());// System.out.println(mmap.position());// System.out.println(mmap.limit());//// mmap.force();// 参考OffsetIndex强制回收已经分配的mmap,不必等到下次GC, unmap(mmap);// 在Windows上需要执行unmap(mmap); 否则报错// Windows won't let us modify the file length while the file is mmapped// java.io.IOException: 请求的操作无法在使用用户映射区域打开的文件上执行 randomAccessFile.setLength(len/2); mmap = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, len/2);// A mapping, once established, is not dependent upon the file channel// that was used to create it. Closing the channel, in particular, has no// effect upon the validity of the mapping. randomAccessFile.close(); mmap.put(128, "z".getBytes()[0]);}// copy from FileChannelImpl#unmap(私有方法) private static void unmap(MappedByteBuffer bb) {undefined Cleaner cl = ((DirectBuffer)bb).cleaner(); if (cl != null) cl.clean();}}
kafka怎么做到基于磁盘却比内存还快
相信看见题目的同学都会很有疑问,甚至不服气,这都是基于个人对于kafka原理的理解,我可以说磁盘顺序写要比内存的随机读快吧。但是说到底,基于性能的优化方面,还是离不开内存的。
Kafka作为一个支持大数据量写入写出的消息队列,由于是基于Scala和Java实现的,而Scala和Java均需要在JVM上运行,所以如果是基于内存的方式,即JVM的堆来进行数据存储则需要开辟很大的堆来支持数据读写,从而会导致GC频繁影响性能。考虑到这些因素,kafka是使用磁盘存储数据的。
Kafka中消息是以 topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的。topic存储结构见下图:
由于生产者生产的消息会不断追加到 log文件末尾,为防止 log文件过大导致数据定位效率低下,Kafka采取了分片和索引机制,将每个partition分为多个segment。每个 segment对应两个文件——“.index”文件和“.log”文件。
partition文件夹命名规则
topic名称+分区序号,举例有一个topic名称文“kafka”,这个topic有三个分区,则每个文件夹命名如下:
index和log文件的命名规则
1)partition文件夹中的第一个segment从0开始,以后每个segement文件以上一个segment文件的最后一条消息的offset+1命名(当前日志中的第一条消息的offset值命名)。
2)数值最大为64位long大小。19位数字字符长度,没有数字用0填充。
举例,有以下三对文件:
以第二个文件为例看下对应的数据结构:
稀疏索引需要注意下。
消息查找过程:
找message-2589,即offset为2589:
1)先定位segment文件,在0000000000000002584中。
2)计算查找的offset在日志文件的相对偏移量 offset-文件名的数量= 2589- 2584= 5;在index文件查找第一个参数的值,若找到,则获取到偏移量,通过偏移量到log文件去找对应偏移量的数据即可;本例中没有找到,则找到当前索引中偏移量的上线最接近的值,即3,偏移量文246;然后到log文件中从偏移量为246数据开始向下寻找。
简单了解了kafka在数据存储方面的知识,线面我们具体分析下为什么kafka基于磁盘却快于内存。
在前面了解存储结构过程中,我们发现kafka记录log日志使用的结尾追加的方式,即顺序写。这样要比随机写块很多,这与磁盘的机械机构有关,顺序写之所以快,是因为其省去了大量磁头寻址的时间。
mmap,简单描述其就是将磁盘文件映射到内存,用户通过修改内存就能修改磁盘文件。
即便是顺序写磁盘,磁盘的读写速度任然比内存慢慢的多得多,好在操作系统已经帮我们解决这个问题。在Linux操作系统中,Linux会将磁盘中的一些数据读取到内存当中,我们称之为内存页。当需要读写硬盘的时候,都优先在内存页中进行处理。当内存页的数据比硬盘数据多的时候,就形成了脏页,当脏页达到一定数量,操作系统会进行刷脏,即将内存也数据写到磁盘。
问题:不可靠,写到 mmap中的数据并没有被真正的写到硬盘,操作系统会在程序主动调用 Flush的时候才把数据真正的写到硬盘。
零拷贝并不是不需要拷贝,而是减少不必要的拷贝次数,通常使用在IO读写过程中。
传统io过程
如上图所示,上图共经历了四次拷贝的过程:
1)数据到内核态的read buffer;
2)内核态的read buffer到用户态应用层的buffer;
3)用户态到内核态的socket buffer;
4)socket buffer到网卡的buffer(NIC)。
DMA引入DMA技术,是指外部设备不通过CPU而直接与系统内存交换数据的接口技术,网卡等硬件设备支持DMA技术。
如上图所示,上图共经历了两次拷贝的过程。
sendfile在内核版本 2.1中,引入了 Sendfile系统调用,以简化网络上和两个本地文件之间的数据传输。同时使用了DMA技术。
如上图所示,上图共经历了一次拷贝的过程。
sendfile( DMA收集拷贝)之前我们是把页缓存的数据拷贝到socket缓存中,实际上,我们仅仅需要把缓冲区描述符传到 socket缓冲区,再把数据长度传过去,这样 DMA控制器直接将页缓存中的数据打包发送到网络中就可以了。
如上图所示,最后一次的拷贝也被消除了,数据->read buffer->NIC。
kafka通过java和scala实现,而Java对sendfile是通过NIO的 FileChannel(
java.nio.channels.FileChannel)的 transferTo和 transferFrom方法实现零拷贝。
注: transferTo和 transferFrom并不保证一定能使用零拷贝。实际上是否能使用零拷贝与操作系统相关,如果操作系统提供 sendfile这样的零拷贝系统调用,则这两个方法会通过这样的系统调用充分利用零拷贝的优势,否则并不能通过这两个方法本身实现零拷贝。
好了,关于java的内存映射io为什么快和内存映射是怎么回事的问题到这里结束啦,希望可以解决您的问题哈!