范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

面试官Netty中的缓冲区为什么比原生NIO更高效

  Netty自己的ByteBuf
  ByteBuf是为了解决ByteBuffer的问题和满足网络应用程序开发人员的日常需求而设计的.
  JDK中ByteBuffer的缺点:无法动态扩容 长度是固定的,不能动态扩展和收缩,当数据大于ByteBuffer容量时,会发生索引越界异常.API使用复杂 读写的时候需要手动调用flip()和rewind()等方法,使用时需要非常谨慎的使用这些api,否则容易出现错误.
  ByteBuf做了哪些增强?API操作便捷性动态扩容多种ByteBuf实现内存复用机制零拷贝机制ByteBuf的操作
  三个重要属性:capacity容量readerIndex读取位置writerIndex写入位置
  提供了两个指针变量来支持顺序读和写操作,分别是readerIndex和writeInDex,也就把缓冲区分成了三个部分:0[ --已读可丢弃区域-- ]reaerIndex[ --可读区域-- ]writerIndex[ --待写区域-- ]capacity
  常用方法定义:随机访问索引getByte顺序读read*顺序写write*清除已读内容discardReadBytes清除缓冲区clear搜索操作标记和重置引用计数和释放
  我们可以对这些api做一些测试,如下:package io.netty.example.echo;  import java.util.Arrays; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled;  /**  * @author daniel  * @version 1.0.0  * @date 2021/12/20  */ public class ApiTest {      public static void main(String[] args) {         //1.创建一个非池化的ByteBuf,大小为10字节         ByteBuf buf = Unpooled.buffer(10);         System.out.println("原始ByteBuf为:" + buf.toString());         System.out.println("1.ByteBuf中的内容为:" + Arrays.toString(buf.array()));         System.out.println();          //2.写入一段内容         byte[] bytes = {1,2,3,4,5};         buf.writeBytes(bytes);         System.out.println("写入的bytes为:" + Arrays.toString(bytes));         System.out.println("写入一段内容后ByteBuf为:" + buf);         System.out.println("2.ByteBuf中的内容为:" + Arrays.toString(buf.array()));         System.out.println();          //3.读取一段内容         byte b1 = buf.readByte();         byte b2 = buf.readByte();         System.out.println("读取的bytes为:" + Arrays.toString(new byte[]{b1, b2}));         System.out.println("读取一段内容后ByteBuf为:" + buf);         System.out.println("3.ByteBuf中的内容为:" + Arrays.toString(buf.array()));         System.out.println();          //4.将读取的内容丢弃         buf.discardReadBytes();         System.out.println("丢弃已读取的内容后ByteBuf为:" + buf);         System.out.println("4.ByteBuf中的内容为:" + Arrays.toString(buf.array()));         System.out.println();          //5.清空读写指针         buf.clear();         System.out.println("清空读写指针后ByteBuf为:" + buf);         System.out.println("5.ByteBuf中的内容为:" + Arrays.toString(buf.array()));         System.out.println();          //6.再次写入一段内容,比第一段内容少         byte[] bytes2 = {1,2,3};         buf.writeBytes(bytes2);         System.out.println("再写入的bytes2为:" + Arrays.toString(bytes2));         System.out.println("再写入一段内容后ByteBuf为:" + buf);         System.out.println("6.ByteBuf中的内容为:" + Arrays.toString(buf.array()));         System.out.println();          //7.将ByteBuf清空         buf.setZero(0, buf.capacity());         System.out.println("内容清空后ByteBuf为:" + buf);         System.out.println("7.ByteBuf中的内容为:" + Arrays.toString(buf.array()));         System.out.println();          //8.再次写入一段超过容量的内容         byte[] bytes3 = {1,2,3,4,5,6,7,8,9,10,11};         buf.writeBytes(bytes3);         System.out.println("写入超量的bytes3为:" + Arrays.toString(bytes3));         System.out.println("写入超量内容后ByteBuf为:" + buf);         System.out.println("8.ByteBuf中的内容为:" + Arrays.toString(buf.array()));         System.out.println();     } }
  从这些api的使用中就可以体会到ByteBuf比ByteBuffer的强大之处,我们可以深入研究一下它在写入超量数据时的扩容机制,也就是buf.writeBytes(byte[])方法ByteBuf动态扩容
  容量默认值为256字节,最大值为Integer.MAX_VALUE,也就是2GB
  实际调用AbstractByteBuf.writeBytes,如下:
  AbstractByteBuf.writeBytes@Override public ByteBuf writeBytes(byte[] src) {     writeBytes(src, 0, src.length);     return this; }
  AbstractByteBuf.writeBytes(src, 0, src.length);@Override public ByteBuf writeBytes(byte[] src, int srcIndex, int length) {     ensureWritable(length); //检查是否有足够的可写空间,是否需要扩容     setBytes(writerIndex, src, srcIndex, length);     writerIndex += length;     return this; }
  AbstractByteBuf.ensureWritable(length);@Override public ByteBuf ensureWritable(int minWritableBytes) {     ensureWritable0(checkPositiveOrZero(minWritableBytes, "minWritableBytes"));     return this; }
  AbstractByteBuf.ensureWritable0(checkPositiveOrZero(minWritableBytes, "minWritableBytes"));final void ensureWritable0(int minWritableBytes) {     final int writerIndex = writerIndex(); //获取当前写下标     final int targetCapacity = writerIndex + minWritableBytes; //计算最少需要的容量     // using non-short-circuit & to reduce branching - this is a hot path and targetCapacity should rarely overflow     if (targetCapacity >= 0 & targetCapacity <= capacity()) { //判断当前容量是否够用         ensureAccessible(); //检查ByteBuf的引用计数,如果为0则不允许继续操作         return;     }     if (checkBounds && (targetCapacity < 0 || targetCapacity > maxCapacity)) { //判断需要的容量是否是合法值,不合法为true直接抛出越界异常         ensureAccessible();//检查ByteBuf的引用计数,如果为0则不允许继续操作         throw new IndexOutOfBoundsException(String.format(                 "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",                 writerIndex, minWritableBytes, maxCapacity, this));     }      // Normalize the target capacity to the power of 2.(标准化为2的次幂)     final int fastWritable = maxFastWritableBytes();     int newCapacity = fastWritable >= minWritableBytes ? writerIndex + fastWritable             : alloc().calculateNewCapacity(targetCapacity, maxCapacity); //计算扩容后容量(只要扩容最小64)      // Adjust to the new capacity.     capacity(newCapacity); //设置新的容量 }
  alloc().calculateNewCapacity(targetCapacity, maxCapacity) -> AbstractByteBufAllocator@Override public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {     checkPositiveOrZero(minNewCapacity, "minNewCapacity"); //最小所需容量     if (minNewCapacity > maxCapacity) { //判断最小所需容量是否合法         throw new IllegalArgumentException(String.format(                 "minNewCapacity: %d (expected: not greater than maxCapacity(%d)",                 minNewCapacity, maxCapacity));     }     final int threshold = CALCULATE_THRESHOLD; // 4 MiB page 阈值超过4M以其他方式计算      if (minNewCapacity == threshold) { //等于4M直接返回4M         return threshold;     }      // If over threshold, do not double but just increase by threshold.     if (minNewCapacity > threshold) { //大于4M,不需要加倍,只需要扩大阈值即可         int newCapacity = minNewCapacity / threshold * threshold;         if (newCapacity > maxCapacity - threshold) {             newCapacity = maxCapacity;         } else {             newCapacity += threshold;         }         return newCapacity;     }      // 64 <= newCapacity is a power of 2 <= threshold     final int newCapacity = MathUtil.findNextPositivePowerOfTwo(Math.max(minNewCapacity, 64)); //计算不少于所需容量的最小的2次幂的值     return Math.min(newCapacity, maxCapacity); //取容量所允许的最大值和计算的2次幂的最小值,当然在这儿就是newCapacity=64 }
  总结一下就是最小所需容量是否等于阈值,如果是直接返回阈值此后直接扩大阈值,否则以64为最小2次幂为基础每次扩大二倍直到阈值.选择合适的ByteBuf实现
  netty针对ByteBuf提供了8中具体的实现方式,如下:
  堆内/堆外
  是否池化
  访问方式
  具体实现类
  备注
  heap堆内
  unpool
  safe
  UnpooledHeapByteBuf
  数组实现
  heap堆内
  unpool
  unsafe
  UnpooledUnsafeHeapByteBuf
  Unsafe类直接操作内存
  heap堆内
  pool
  safe
  PooledHeapByteBuf
  heap堆内
  pool
  unsafe
  PooledUnsafeHeapByteBuf
  ~
  direct堆外
  unpool
  safe
  UnpooledDirectByteBuf
  NIO DirectByteBuffer
  direct堆外
  unpool
  unsafe
  UnpooleUnsafedDirectByteBuf
  ~
  direct堆外
  pool
  safe
  PooledDirectByteBuf
  ~
  direct堆外
  pool
  unsafe
  PooledUnsafeDirectByteBuf
  ~
  在使用时,都是通过ByteBufAllocator分配器进行申请,同时分配器具有内存管理的功能。
  在这儿堆内和堆外没有什么区别,对api的使用时一样的,仅仅是通过Unpooled申请的不一样.
  那个safe和unsafe有什么区别呢?
  以UnpooledHeapByteBuf和UnpooledUnsafeHeapByteBuf中的getByte(int index)方法为例进行分析
  UnpooledHeapByteBuf@Override public byte getByte(int index) {     ensureAccessible();     return _getByte(index); //真正的获取字节的方法 }  @Override protected byte _getByte(int index) {     return HeapByteBufUtil.getByte(array, index);  //通过HeapByteBufUtil工具类获取数据 }
  HeapByteBufUtilstatic byte getByte(byte[] memory, int index) {     return memory[index]; }
  UnpooledHeapByteBuf从堆内数组中获取数据,这是安全的
  UnpooledUnsafeHeapByteBuf @Override public byte getByte(int index) {     checkIndex(index);     return _getByte(index); }  @Override protected byte _getByte(int index) {     return UnsafeByteBufUtil.getByte(array, index); }
  PlatformDependent0static byte getByte(byte[] data, int index) {     return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index); }
  UnpooledUnsafeHeapByteBuf是通过UNSAFE来操作内存的
  现在我们来研究一下UnsafeUnsafe的实现
  Unsafe意味着不安全的操作,但是更底层的操作会带来性能提升和特殊功能,Netty中会尽力使用unsafe以提升系统性能
  Java语言很重要的特性就是一次编译到处运行,所以它针对底层的内存或者其他操作做了很多封装,而unsafe提供了一系列我们操作底层的方法,可能会导致不兼容或不可知的异常.
  比如:返回一些低级的内存信息addressSizepageSize提供用于操作对象及其字段的方法allocateInstanceobjectFieldOffset提供用于操作类及其静态字段的方法staticFieldOffsetdefineClassdefineAnonymousClassensureClassInitialized低级的同步原语monitorEntertryMonitorEntermonitorExitcompareAndSwapIntputOrderedInt直接访问内存的方法 allocateMomery copyMemory freeMemory getAddress getInt putInt操作数组arrayBaseOffsetarrayIndexScale
  既然这些东西都是jdk封装好的,而是netty也是直接使用的,所以我们无论在使用safe还是unsafe的时候都是无感知的,我们无需关系底层的操作逻辑,因为api都是一样的,只是实现不一样
  是否还有一个疑问,池化和非池化是什么意思?池化和非池化
  比如在使用Unpooled.buffer(10)申请一个缓存区的时候,默认非池化申请的一个缓冲区.
  池化和非池化的区别主要是申请内存缓存空间以及缓存空间的使用上,体现为内存复用.在申请内存缓存空间方面:pool:池化申请的时候会申请一个比当前所需内存空间更大的内存空间,这就好比一个快递柜,为此netty提供了buf分配管理器专门用来处理这种事情,来创建或复用ByteBuf.unpool:非池化申请只会申请特定大小能够使用的内存缓存空间,使用完之后立刻释放,这就像直接把快递放到你的手中,你所在的位置就是开辟的内存空间.在缓存空间使用方面:pool:池化申请的内存空间有一定扩容容积,也就是这个快递柜可以存放多个快递,只需要找到对应的方格即可存放,同样buf分配管理器来复用已经创建好的内存空间,在创建ByteBuf的时候已经开辟3中大小的内存块 normal:16MN small:8KB tiny:512Bunpool:毫无疑问,非池化的方式必然是每次都会再去开辟内存空间的.
  理论如此,netty中是如何做到内存复用的?
  在netty中每一个EventLoopThread由PooledBytebufAllocator内存分配器实力维护了一个线程变量叫做PoolThreadCache,在这个变量中维护了3种规格的MemoryRegionCache数组用作内存缓存,MemoryRegionCache内部是链表,队列里面存Chunk.
  首先内存内存分配器会寻找合适的ByteBuf对象进行复用;
  之后从内存数组中找到合适的内存空间进行复用;
  PoolChunk里面维护了内存引用,内存复用的做法就是把ByteBuf的memory指向chunk的memory.
  如果没有找到对应的缓存空间,则直接向内存申请unpool的缓存空间.
  netty中默认(池化)也是这样做的,这也是netty性能高效的一个原因,但是就像example例子一样,如果我们自己创建的话,netty推荐我们使用unpool.
  ==需要注意的是即使创建了可复用的ByteBuf,但是使用过后一直没有被release,也就是没有被回收也是不能被复用的,这是应用设计时应该注意的.==
  说了半天的废话,总算是要说到零拷贝机制了零拷贝机制
  Netty的零拷贝机制是一种应用层的表现,和底层JVM/操作系统内存机制并无过多关联,你可认为netty就是一个软件,我们在用这个软件来创造另一个软件.CompositeByteBuf,将多个ByteBuf合并为一个逻辑上的ByteBuf,避免了各个ByteBuf之间的拷贝 什么意思呢?这是一个虚拟的ByteBuf,这个ByteBuf并不是一个,而是一个复合缓冲区,有多个独立的ByteBuf CompositeByteBuf compositeByteBuf = Unpooled.CompositeBuffer(); ByteBuf byteBuf = compositeByteBuf.addComponents(true,buffer1,buffer2); 复制代码wrapedBuffer()方法,将byte[]数组包装成ByteBuf对象 什么意思呢?这也是一个虚拟的ByteBuf,这个新创建的ByteBuf只是通过memory对此字节数组做了一个引用,避免了复制带来的性能损耗. ByteBuf byteBuf = Unpooled.wrappedBuffer(new byte[]{1,2,3,4,5}); 复制代码slice()方法,将一个ByteBuf对象切分成多个ByteBuf对象 什么意思呢?这还是一个虚拟的ByteBuf,只不过拆分出去的ByteBuf中的memory引用的只是拆分出去的字节位置,并且会以unwarp保留一个对原ByteBuf的引用. ByteBuf byteBuf = Unpooled.wrappedBuffer("hello".getBytes()); ByteBuf newByteBuf = byteBuf.slice(1,2); 复制代码
  ByteBuf的零拷贝机制也是Netty高性能的一个原因.
  作者:梧桐小站
  链接:https://juejin.cn/post/7049754668653608997

智能扫地机器人推荐哪个牌子?又该怎么选?没有买过扫地机器人的朋友都会问扫地机器人什么牌子好?扫地机器人好用吗?选到好的扫地机肯定是好用的,关于扫地机器人选购常识和扫地机器人推荐,看我这篇就足够了!需要了解扫地机器人的这些SEGOO小洁扫地机器人怎么样?一天工作下来,已经很累了,如果还要动手搞卫生的话,哪属于自己的空闲时间就更少了,但是如果不搞卫生的话,那么脏兮兮的环境,也会影响你的心情。扫地机器人的出现,真的解放了双手,现在扫地扫地机选哪个好?选择困难症看过来相对于传统的清洁工具而言,智能清洁工具已经慢慢进入大家的生活了,像扫地机器人这样富有科技感的智能家居产品,已经进入了大众是视野,给大家的家务清洁带来巨大的便利。但是市面上扫地机品牌实测SEGOO小洁扫地机器人,真的有那么好吗?以扫地机器人为代表的清洁电器,或者又被认为家庭清洁服务类机器人产品,突然于一线市场和年轻消费群体间全面走红。扫地机器人的出现,能够让我们真正享受到一个干净无尘的安宁空间最近小洁作为选择扫地机器人最主要的点,你一定要知道随着智能化扫地机器人进入到越来越多人的家中,身边很多朋友都开始入手扫地机器人了。但是市面上扫地机器人这么多,品牌多不胜数,那么我们怎样购买智能化扫地机器人呢?怎么才能够不踩雷呢?一扫地机该如何选择?最全选购攻略随着科技的发展,智能产品逐渐普及到我们的生活。扫地机作为解放双手的神器,成为了众多人群的首选。相信很少有人天生爱做家务,如果你家的房子很大,那就推荐你购买一台扫地机器人。或者是工作SEGOO扫地机器人使用报告扫拖一体,家务尽在掌握随着科技的发展,2021年扫地机器人的使用需求也在逐渐上升,产品功能方面也在不断做出着升级,逐渐成为一般家庭不可或缺的实用家务清洁电器。SEGOO小洁,这个走在扫地机器人领域的先行人工智能的餐厅到底长什么样?网络上曾经有一段搞笑的无厘头我要去吃麦当劳了,怎样才能装的像是经常去的?有什么要注意的?放在以前,这是个笑话,但是到现在,去麦当劳或者肯德基,真的要先学一学,适应一下了,因为它们现2018中国吉林国际汉语读书大赛记者见面会为响应一带一路倡议,促进国际间文化互学互鉴,4月12日上午,在东北亚艺术中心举办2018中国吉林国际汉语读书大赛暨东北亚书城国际汉语读书季记者见面会。记者见面会本次大赛分为在华外籍中国移动IPO获批,运营商聚齐A股,增量也要增收啊11月4日,据中国证监会第十八届发审委2021年度第119次会议审核结果公告,中国移动首发申请获通过。招股书显示,中国移动拟公开发行人民币股份数量不超过9。65亿股并在上交所主板上在朋友家种草了小洁M2扫地机器人,十分照顾用户使用体验平常扫地拖地都是需要我们弯腰驼背的,加上家里比较大,一顿操作下来,想想都让人觉得身心疲惫。后来在朋友家种草了一款SEGOO小洁M2扫地机器人,正好趁着有优惠活动下单了。我是活动价2
vivo蔡司影像展上海站以专业致敬专业,回忆影像故事vivo去年和蔡司联合定制了vivoX60系列的摄像头方案,从蔡司色彩到蔡司T镀膜,vivo用vivoX60系列在手机影像中掀起了一股浪潮。今日,vivo携手全球知名影像光学品牌蔡联想美好假期温暖2021,总有一个故事会打动你这是2021年第一份套餐A,2021年我们还会将套餐A活动继续做下去,继续将爱心传递下去,在济南街头,玖壹粉面馆老板对于联想美好假期志愿者们的到来,既惊喜又意外。(套餐A的内容)其女儿为何更喜欢爸爸,难道真的是上辈子的情人?白苏说文经常听别人说女儿是爸爸前世的情人。以前还没怎么体会到这种感觉,直到女儿两岁,我才稍微有一点点体会,不过小家伙给我的感觉很纯粹,很特别她那么小,对待爸爸和妈妈竟然完全不一样,550T版新增48V轻混系统2022款雪佛兰探界者上市配置升级日前,上汽通用雪佛兰官方宣布,旗下的2022款探界者车型上市,新车推出6款车型可选,官方指导价区间为16。99万元23。99万元。作为年度改款车型,新车在外形方面并无调整,前脸依然显卡价格高涨是做等等党还是先行入手?这个答案也许会有启发自打进入三月中旬以后,显卡的价格依然没有掉头向下的迹象,相反的是随着RTX3060密算限制解除驱动的意外挂出,导致玩家的希望RTX3060也一路水涨船高。现在的显卡市场状况如何呢?元宇宙第一股Roblox在停机三天后重新上线编译VR陀螺Roblox在持续三天的大停机后于美国东部时间周日恢复了服务。Roblox是一个允许用户玩和创建游戏的在线平台,它在美国东部时间10月28日星期四晚上7点左右开始出现问Admix获2500万美元B轮融资,正通过InPlay实现元宇宙货币化编译VR陀螺本周,专门为VRAR开发者提供程序化广告解决方案的Admix公司宣布完成2500万美元B轮融资。本轮融资由Elefund与DIPCapital领投,ForceOverM元宇宙前所未有的真实可触从NVIDIASIGGRAPH造假纪录片谈起所谓元宇宙(Metaverse),其实是虚拟现实世界的另一种称呼,有时候特指的是融入了社交功能的三维游戏,2018年的电影头号玩家(PlayerOne)里的游戏就是元宇宙的一种表现咪咕发布元宇宙MIGU演进路线图,将通过5GMSC,打造体育元宇宙VR陀螺11月3日讯,11月2日下午,以云上精彩,共创数智生活为主题的2021中国移动全球合作伙伴大会产品创新融合发布会在广州召开。中国移动咪咕公司总经理刘昕以搭乘5G信息高铁加速命名奇骏经典?老款日产奇骏有望复产缓解销量危机据国内媒体报道,日产旗下的老款奇骏车型有望复产,未来会命名为经典版奇骏,和全新奇骏同堂销售。配图为2020款奇骏老款车型复产的原因显然和全新一代奇骏销量表现不算给力有关系,从乘联会国产机械键盘推荐,谁是你心中的国货之光呢?机械键盘的市场愈发成熟,各种各样的产品层出不穷,大众或被动或主动地对机械键盘也有了更深的了解。而外设领域中国外品牌一直占主导地位,但是近几年,有许多国产品牌因为精湛的工艺和过硬的质