nio学习

本文介绍了Java NIO如何利用堆外内存进行高效存储和访问大对象,如通过MemoryMapping处理Trie树。NIO提供双向通道、精确的缓冲区操作以及非阻塞I/O支持,减少了数据拷贝,适合处理大数据。示例代码展示了如何使用NIO的FileChannel和MappedByteBuffer进行读写操作。
摘要由CSDN通过智能技术生成

最近在项目中存储一个内存对象,这个内存对象比较大,于是把它放在文件,通过内存映射的方式访问这个对象。

于是用到了这个nio,因为他可以开辟堆外内存

堆外内存(说非堆不太准确,毕竟非堆区域不止这一块),时分配在C Heap上的Buffer,由于不属于JVM HEAP,管理/监控起来会比较困难,但也会被GC回收。但他是通过PhantomReference来达到的,正常的young gc或者mark and compact的时候不会在内存里移动。DirectByteBuffer 自身是(Java)堆内的,它背后真正承载数据的buffer是在(Java)堆外——native memory中的。这是 malloc() 分配出来的内存,是用户态的。(来自于Java中的Heap Buffer与Direct Buffer - SegmentFault 思否

我们使用普通的ByteBuffer,那么这个ByteBuffer就会在Java堆内,被JVM所管理当 buf 被JVM所管理时,它会在变成垃圾以后,自动被回收,这个不需要我们来操心。但它有个问题,后面会讲到,在执行GC的时候,JVM实际上会做一些整理内存的工作,也就说buf这个对象在内存中的实际地址是会发生变化的。有些时候,ByteBuffer里都是大量的字节,这些字节在JVM GC整理内存时就显得很笨重,把它们在内存中拷来拷去显然不是一个好主意。(来自于java directBuffer - 搜索结果 - 知乎

明白这个堆外内存之后,我们就可以创建一个比较大的对象,因为它的地址不会因为GC整理而改变,那么我们就能通过Nio来做MemoryMap.例如把Tire树,写入文件

public class TrieNode {
    private byte key;
    private String word;
    private int wordSize;
    private int currentOffset;
    private HashMap<Byte, TrieNode> children;


  public void write(FileChannel channel, int[] offset) {
        try {
            long currentOffset = offset[0];
            offset[0] = offset[0] + size();
            ByteBuffer buffer = ByteBuffer.allocateDirect(size());
            buffer.put(key);
            buffer.putShort((short) (wordSize));
            buffer.putShort((short) children.keySet().size());
            for (byte k : children.keySet()) {
                TrieNode node = children.get(k);
                node.write(channel, offset);
                buffer.putInt(offset[0]);
            }
            if (word != null && wordSize > 0) {
                buffer.put(word.getBytes());
            }
            buffer.flip();
            channel.write(buffer, currentOffset);

        } catch (IOException e) {
            LogUtil.d("___异常" + e.getCause());
            e.printStackTrace();
        }
    }     
   private int size() {
        int size = 1 + 2 + 2 + children.keySet().size() * 4 + wordSize;
        return size;
    }
}

之后我们想访问这个树,可以通过FileChanel 获取MappedByteBuffer来访问。这样就不用把整个树加载到内存里了。

传统io是插管道,单项流,要么输出流,要么输入流

到这里是第一次去使用Nio,但是Nio的其他用法
1.它是双向的,(轨道和火车的比喻很象形,channel是轨道,buffer是火车)

2.它强制使用buffer,还可以精细操作buffer

3.有非阻塞式支持,仅仅网络交互支持非阻塞式。

简单的使用:

 public static void main(String[] args) {
	// write your code here
        try {
            RandomAccessFile r = new RandomAccessFile("./iocompare/text.txt", "r");
            FileChannel channel = r.getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            channel.read(byteBuffer);
            byteBuffer.flip();//
            Charset.defaultCharset().decode(byteBuffer);
            byteBuffer.clear()
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

这个channel.read,对一1024个字节到bytebuffer里,同样也可以写channel.write。
有了第一个特点既可以读也可以写

2.可以精确操作buffer,就是position, limit,mark,capcity

3.支持非阻塞流

   try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(8080));
//            serverSocketChannel.configureBlocking(false);
            SocketChannel socketChannel = serverSocketChannel.accept();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            if (socketChannel.read(byteBuffer) !=-1) {
                byteBuffer.flip();
                socketChannel.write(byteBuffer);
                byteBuffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

这里的使用场景是什么呢?非阻塞,有点像Future,如果不阻塞去拿,拿到的结果肯定是空的,Future的非阻塞场景,我想应该是做兼容的时候用。
另一个也很重要的方面,它在拷贝数据方面很有优势,少一层从内核态到用户态的数据拷贝,所以可以在拷贝大数据时可以考虑使用

Java 中 NIO 看这一篇就够了 - 知乎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值