理解java中的mmap

jdk中的MappedByteBuffer非常类似linux中的mmap将文件映射到虚拟内存,这样可以利用操作系统自带的虚拟内存实现来

提高io效率, 很多文档中提到这一点,具体大家可以自行测试。


MappedByteBuffer有3种映射模式:

MapMode.READ_ONLY:只读映射

MapMode.READ_WRITE​:读写映射

MapMode.PRIVATE​:copy on write映射,一旦需要改变原有buffer,拷贝一份原来的


本文通过一种测试代码来理解MappedByteBuffer使用MapMode.READ_WRITE以及MapMode.PRIVATE,特别是MAP_MODE.PRIVATE模式​


public class MMapTest {

	private static FileChannel channel;

	@BeforeClass
	public static void runBeforeClass() throws FileNotFoundException {
		String path = MMapTest.class.getResource("/test.txt").getPath();
		File file = new File(path);
		channel = new RandomAccessFile(file, "rw").getChannel();

	}

	@AfterClass
	public static void runAfterClass() throws IOException {
		channel.close();
	}

	@Before
	public void runBeforeEveryTest() throws IOException {
		ByteBuffer temp = ByteBuffer.allocate(100);
		temp.put("12345".getBytes());
		temp.flip();
		channel.truncate(5);
		channel.write(temp);
	}

	/**
	 * 
	 * @author weip.pengw
	 * @time 2012-3-19 下午02:53:21
	 * @param buffer
	 * @throws Exception
	 */
	public static String dumpBuffer(ByteBuffer buffer) throws Exception {

		StringBuffer sb = new StringBuffer();
		int nulls = 0;
		int limit = buffer.limit();
		for (int i = 0; i < limit; i++) {
			char c = (char) buffer.get(i);
			if (c == '\u0000') {
				nulls++;
				continue;
			}

			if (nulls != 0) {
				sb.append("|[" + nulls + " nulls]|");
				nulls = 0;
			}
			sb.append(c);
		}
		return sb.toString();
	}

	/**
	 * 
	 * @author weip.pengw
	 * @throws Exception
	 * @time 2012-3-19 下午02:45:28
	 */
	@Test
	public void testCopyOnWrite() throws Exception {
		// 产生一个文件,文件跨内存页
		ByteBuffer temp = ByteBuffer.allocate(100);
		temp.put("This is the file content".getBytes());
		temp.flip();
		channel.write(temp, 0);
		temp.clear();
		temp.put("This is more file content".getBytes());
		temp.flip();
		channel.write(temp, 8192);

		// 产生两个MAPFILE
		MappedByteBuffer rw = channel.map(FileChannel.MapMode.READ_WRITE, 0,
				channel.size());
		MappedByteBuffer cow = channel.map(FileChannel.MapMode.PRIVATE, 0,
				channel.size());

		// 测试之前
		assertThat(
				dumpBuffer(rw),
				is("This is the file content|[8168 nulls]|This is more file content"));
		assertThat(
				dumpBuffer(cow),
				is("This is the file content|[8168 nulls]|This is more file content"));

		// 测试step1,修改rw前几个字节
		rw.position(0);
		rw.put("RW".getBytes());
		rw.force();
		assertThat(
				dumpBuffer(rw),
				is("RWis is the file content|[8168 nulls]|This is more file content"));
		assertThat(
				dumpBuffer(cow),
				is("RWis is the file content|[8168 nulls]|This is more file content"));

		// 测试step2,修改cow前几个字节,触发copy on write
		cow.position(0);
		cow.put("COW".getBytes());
		assertThat(
				dumpBuffer(rw),
				is("RWis is the file content|[8168 nulls]|This is more file content"));
		assertThat(
				dumpBuffer(cow),
				is("COWs is the file content|[8168 nulls]|This is more file content"));

		// 测试step3,修改rw的最后几个字节,cow后面的字节反应了改变
		rw.position(8192);
		rw.put("RW".getBytes());
		rw.force();
		assertThat(
				dumpBuffer(rw),
				is("RWis is the file content|[8168 nulls]|RWis is more file content"));
		assertThat(
				dumpBuffer(cow),
				is("COWs is the file content|[8168 nulls]|RWis is more file content"));

		// 测试step4,修改cow的最后几个字节,再次触发copy on write
		cow.position(8192);
		cow.put("COW".getBytes());
		assertThat(
				dumpBuffer(rw),
				is("RWis is the file content|[8168 nulls]|RWis is more file content"));
		assertThat(
				dumpBuffer(cow),
				is("COWs is the file content|[8168 nulls]|COWs is more file content"));

		// 测试step5,再次修改rw的前后几个字节,对cow没有了影响
		rw.position(0);
		rw.put("RW2".getBytes());
		rw.position(8192);
		rw.put("RW2".getBytes());
		rw.force();
		assertThat(
				dumpBuffer(rw),
				is("RW2s is the file content|[8168 nulls]|RW2s is more file content"));
		assertThat(
				dumpBuffer(cow),
				is("COWs is the file content|[8168 nulls]|COWs is more file content"));

		// cleanup
		// channel.close();

	}


重点看看代码中的测试方法testCopyOnWrite

1)产生一个较大空洞文件(本文操过8k),使得操作系统(代码在ubuntu10.04测试通过)无法在一页能映射该文件(跨内存页),这样才会有测试效果

2)将文件映射到两个MappedByteBuffer实例,一个是MapMode.READ_WRITE模式简称rw,一个是FileChannel.MapMode.PRIVATE模式简称cow

3) 测试之前,两个buff实例内容一致

4)第1步测试修改rw实例,由于没有触发copy on write,所以两个buff实例内容一致

5) 第2步测试修改cow实例的前几个字节,cow的修改只影响它本身,因为它修改的时候实际上重新拷贝了一份

6)第3步测试修改rw实例的后几个字节,居然在cow实例中能看到,之前不是说cow拷贝了一份吗?原因后面说

7)第4步测试修改cow实例的后几个字节,​cow的修改只影响它本身

8)第5步测试修改rw实例的前后字节,发现都没反映在cow实例中,的确和6)有所不同。

这说明:

  • 一开始没有修改cow,会共用rw的内存,
  • 当cow修改了前几个字节,cow会拷贝内存,但明显cow实例是按内存页拷贝的,由于文件较长,所以只拷贝了前一个部分,前面的是自己的,后面是共享的
  • ​当cow修改了后几个字节​,cow才拷贝后面的内存页,这样的话,bw对文件后面的修改,cow也就看不到了

 


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值