Netty应用第四篇:通过ByteBuffer了解Buffer的一系列内容

目录

本文须知

前言

ByteBuffer的实现

内存溢出和内存泄漏

ByteBuffer的获取

ByteBuffer的核心结构

三个主要状态

数据的读写

核心API

总结


本文须知

        本文内容来自于B站孙帅suns的netty应用视频,由本人根据视频学习整理而成。然而,由于个人理解和整理的差异,可能存在部分地方的误差或讲解不够明确的情况。

        为了获得更详细的课程内容和准确的解释,请您移步至B站,搜索孙帅suns获取详细课程视频内容,以便深入了解和学习。孙帅suns的视频将为您提供更全面、详细的知识和解释,帮助您更好地理解netty应用的相关内容。

        在阅读本文时,请注意本文的目的是为了提供一个概述和个人整理的观点,而不是取代原始视频课程的内容。如有任何疑问或不明之处,建议您参考原始视频课程以获取更准确的信息。

        感谢您的理解和支持!

        在此附上B站孙帅suns个人主页链接:孙帅suns个人主页

前言

上一篇我和大家一起了解了Channel和Buffer。我们知道,Buffer当中存在很多种类,但是大多都是相通的,所以这一篇我们通过ByteBuffer来了解一下Buffer的获取和实现,以及它的一些核心API。

ByteBuffer的实现

ByteBuffer是抽象类,他的主要实现类有

  • HeapByteBuffer
  • MappedByteBuffer(DirectByteBuffer)

HeapByteBuffer和DirectByteBuffer的区别

  • HeapByteffer使用的是JVM内的内存
    • 优点:
      • 创建成本低;
    • 缺点:
      • 读写效率低;
      • 会受到gc的影响;
  • DirectByteBuffer使用的是OS的内存
    • 优点:
      • 读写效率高,不会受到gc的影响,不会受到Java堆内存大小的限制;
    • 缺点:
      • 需要主动的进行析构(资源回收),否则会造成内存的泄露;

内存溢出和内存泄漏

内存溢出

  • 指的是可提供的内存容量不足以满足程序需求申请的内存大小。

内存溢出通常由程序或者算法问题引起,如:

  • 无限递归;
  • 创建过多对象;

注意:内存溢出通常发生在堆栈内存上。

内存泄漏

  • 指的是因为部分原因,导致可提供的内存大小并不是真实的内容容量。比如有一部分内存被程序申请使用之后没有进行正确的释放或回收。

内存泄漏通常由于程序当中的错误或疏忽引起,如:

  • 忘记释放动态分配的内存;
  • 循环引用;

注意:内存泄漏通常发生在堆内存上。

ByteBuffer的获取

获取方式

  • ByteBuffer.allocate(10);
    • 一旦分配内存空间,无法进行动态的调整。
    • 这里在netty当中提供的ByteBuffer可以动态的调整。原始的NIO当中的ByteBuffer无法进行调整。
  • encode()

ByteBuffer的核心结构

三个主要状态

ByteBuffer是一个类似数组的结构,整个结构当中包含三个主要的状态:

  1. Capacity:
    1. buffer的容量,类似于数组的size
  2. Position:
    1. buffer当前缓存的下标,在读取操作时记录读到了哪个位置,在写操作时记录写到了哪个位置。从0开始,每读取一次,下标+1
    2. 注意:每次进行读写的时候需要检查和控制position的位置,确保不会越界访问缓冲区当中的数据。
  3.  Limit:
    1. 读写限制,在读操作时,设置你能够读取多少字节数据;在写操作时,设置你还能写多少字节数据。

//测试刚开始写操作时
@Test
public void testState1() {
    ByteBuffer buffer = ByteBuffer.allocate(10);

    //10 0 10
    System.out.println("buffer.capacity() = " + buffer.capacity());
    System.out.println("buffer.position() = " + buffer.position());
    System.out.println("buffer.limit() = " + buffer.limit());
}	

//测试写进三位之后的变化
@Test
public void testState2() {
    ByteBuffer buffer = ByteBuffer.allocate(10);

    buffer.put(new byte[]{'a', 'b', 'c'});

    //10 3 10
    System.out.println("buffer.capacity() = " + buffer.capacity());
    System.out.println("buffer.position() = " + buffer.position());
    System.out.println("buffer.limit() = " + buffer.limit());

    //追加测试,再写两位
    System.out.println("==================================");
    buffer.put(new byte[]{'1', '2'});

    //10 5 10
    System.out.println("buffer.capacity() = " + buffer.capacity());
    System.out.println("buffer.position() = " + buffer.position());
    System.out.println("buffer.limit() = " + buffer.limit());
}

//测试刚开始读操作时的变化
@Test
public void testState3() {
    ByteBuffer buffer = ByteBuffer.allocate(10);

    buffer.flip();

    //10 0 0
    System.out.println("buffer.capacity() = " + buffer.capacity());
    System.out.println("buffer.position() = " + buffer.position());
    System.out.println("buffer.limit() = " + buffer.limit());
}

//测试写进三个数然后读状态下的变化
@Test
public void testState4() {
    ByteBuffer buffer = ByteBuffer.allocate(10);

    buffer.put(new byte[]{'a', 'b', 'c'});
    buffer.flip();

    //10 0 3
    System.out.println("buffer.capacity() = " + buffer.capacity());
    System.out.println("buffer.position() = " + buffer.position());
    System.out.println("buffer.limit() = " + buffer.limit());
}

数据的读写

在NIO当中,写入Buffer之前要设置写模式

  1. 新创建的buffer是自动是写模式;
  2. 调用clear、compact方法可以切换成写模式;

在NIO当中,读取Buffer之前要设置读模式

  1. 调用flib方法;

clear和compact方法的区别:

  • clear:清空缓冲区,将缓冲区设置成写模式;
  • compact:切换到写模式,同时将未读取的数据移动到缓冲区的开头。

测试compact方法:

/** 测试compact方法
 *   作用:将未读取的数据移动到缓冲区的开头,之前已读取的数据所占用的空间就变得可以重新使用,从而减少了内存的浪费。
 */
@Test
public void testStatic5() {
    ByteBuffer buffer = ByteBuffer.allocate(10);
    buffer.put(new byte[]{'a', 'b', 'c', 'd'});

    //设置读模式,同时取出两位
    buffer.flip();
    System.out.println("buffer.get() = " + (char)buffer.get()); //a
    System.out.println("buffer.get() = " + (char)buffer.get()); //b

    //测试取出两位之后的状态:10 2 4
    System.out.println("buffer.capacity() = " + buffer.capacity());
    System.out.println("buffer.position() = " + buffer.position());
    System.out.println("buffer.limit() = " + buffer.limit());


    //通过compact设置成写模式之后再查看状态:10 2 10
    System.out.println("=====================");
    buffer.compact();
    System.out.println("buffer.capacity() = " + buffer.capacity());
    System.out.println("buffer.position() = " + buffer.position());
    System.out.println("buffer.limit() = " + buffer.limit());
}

核心API

Buffer当中写数据

  1. channel的read方法:
    1. channel.read(buffer)
  2. buffer的put方法
    1. buffer.put(byte)
    2. byffer.put(byte[])

Buffer当中读数据

  1. channel的write方法
  2. buffer的get方法
    1. 每调用一次get方法,会影响position的位置
  3. rewind方法
    1. 可以将position重置成0,用于复读数据
  4. mark&reset方法
    1. 通过mark方法进行标记position
    2. 通过reset方法跳回标记,重新执行
  5. get(i)方法:
    1. 获取特定position上的数据,但是不会对position的位置产生影响
    2. 同时get(i)方法不受读写模式的影响

下面是关于上述一些方法的测试:

//测试mark和reset
@Test
public void TestAPI1() {
    ByteBuffer buffer = ByteBuffer.allocate(10);
    buffer.put(new byte[]{'a', 'b', 'c', 'd'});

    buffer.flip();
    System.out.println("buffer.get() = " + (char)buffer.get()); //a
    System.out.println("buffer.get() = " + (char)buffer.get()); //b

    buffer.mark(); //标记
    System.out.println("buffer.get() = " + (char)buffer.get()); //c
    System.out.println("buffer.get() = " + (char)buffer.get()); //d

    buffer.reset();
    System.out.println("==============");
    System.out.println("buffer.get() = " + (char)buffer.get());
    System.out.println("buffer.get() = " + (char)buffer.get());
}

//测试get(i)方法
@Test
public void TestAPI2() {
    ByteBuffer buffer = ByteBuffer.allocate(10);
    buffer.put(new byte[]{'a', 'b', 'c', 'd'});

    buffer.flip();
    System.out.println("buffer.get(2) = " + (char)buffer.get(2));
}

总结

        在Buffer当中,其实最重要的就是三个状态,对Buffer的操作基本上都跟这三个状态有关,所以,只需要我们能够熟练掌握这三个状态,基本上就能够熟练的运用Buffer了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ikwil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值