java nio 应用场景_JAVA IO专题三:java的内存映射和应用场景

相关java IO专题

mmap的作用

mmap的作用,是将文件的一部分直接映射到内存(堆外内存),对这个映射的操作会由操作系统在某个特定的时期自动将脏页写回文件对应的位置(也可以通过msync强制写回),而不必调用read/write。mapp完成后,OS并没有直接读取文件的内容,而是在真正要访问的时候,通过缺页异常来进行读磁盘操作。

mmap相比普通的文件读写优势在哪

常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。

而mmap实现了将设备驱动在内核空间的部分地址直接映射到用户空间,使得用户程序可以直接访问和操作相应的内容,减少了额外的拷贝。

说白了,mmap的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不同数据不通的繁琐过程。因此mmap效率更高。

mmap不适合的场景

mmap也不是万能的:

mmap内存映射的大小始终是整数页,因此对于文件实际大小和映射的空间之间多少回有差异,这个差异的空间是被浪费的,对于小文件来说这个浪费比例被放大,因此mmap更适合大文件。

频繁映射大量不同大小的内存,会导致内存碎片化。

java中mmap的应用

java中提供了MappedByteBuffer支持mmap的调用,其本身是一个DirectByteBuffer,即堆外内存。获取MappedByteBuffer方式如下:

MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);

复制代码

另外MappedByteBuffer扩展了一个force方法,强制将buffer中的内容写入磁盘。

java内存映射的应用场景

消息队列的场景非常契合前面说到的mmap的优势,并且能够完美避开其劣势。java开源消息队列RocketMQ和QMQ都采用了MappedByteBuffer写消息数据,以顺序写的方式提高吞吐量。这里举QMQ为例(RocketMQ笔者不太了解hhh):

所有的消息都用LogSegment来管理,一个LogSegment对应磁盘上的一个文件,持有一个fileChannel和

mappedByteBuffer,并且每次都会记录最后写入的位置AtomicInteger wrotePosition = new AtomicInteger(0);

mappedByteBuffer由channel.map获得,fileSize默认都是1G。

写数据的时候会通过mappedByteBuffer.slice获取targetByteBuffer(没有直接写到mappedByteBuffer是因为??),并通过 buffer.position(currentPos);将写指针移动到上次写的位置,然后将数据追加到后面:

//这里的workingBuffer是堆内存,初始化了一些header数据

targetBuffer.put(workingBuffer.array(), 0, headerSize);

//这里的nioBuffer是netty接收到的消息,默认为堆外内存

targetBuffer.put(message.getBody().nioBuffer());

复制代码

另外在PeriodicFlushService中开启一个定时任务,每隔一定时间(默认500ms)将buffer数据刷盘(通过buffer.force())。

参考文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值