ByteBuffer的内存分配

本文详细介绍了ByteBuffer的三种内存分配方式:直接分配、非直接分配和池化分配,以及Netty中的PooledByteBufAllocator和UnpooledByteBufAllocator的区别和适用场景。直接分配提供高IO性能,非直接分配成本低且可能利用缓存,池化分配通过重复利用内存提高性能。
摘要由CSDN通过智能技术生成

内存分配的几种方式

ByteBuffer的内存分配方式主要有三种:直接分配、非直接分配和池化分配。

  1. 直接分配(Direct Allocation): 直接分配是通过调用ByteBuffer.allocateDirect()方法来创建的。在直接分配的情况下,ByteBuffer的底层存储空间会直接由操作系统分配,并且在Java堆之外。这种方式可以提供更高的IO性能,因为数据可以直接从磁盘或网络传输到ByteBuffer中,而不需要进行额外的内存拷贝。此外,直接分配的ByteBuffer还具有较大的容量限制,因为它们依赖于底层操作系统的分页机制来管理内存

  2. 非直接分配(Non-direct Allocation): 非直接分配是通过调用ByteBuffer.allocate()方法来创建的,也是默认的分配方式。在非直接分配的情况下,ByteBuffer的底层存储空间是由Java堆中的字节数组(byte array)来管理的。这种方式相对于直接分配来说,具有更低的分配和释放成本,并且在某些情况下可以更好地利用缓存。然而,由于数据需要从字节数组复制到直接分配的ByteBuffer中,因此在进行IO操作时可能会产生额外的内存拷贝

  3. 池化分配(Pooled Allocation): 池化分配是一种更高级的内存分配方式,它通过使用内存池来管理ByteBuffer的分配和释放。在池化分配中,ByteBuffer实例被重复使用,而不是每次都创建新的实例。这种方式可以避免频繁的内存分配和垃圾回收,从而提高性能和降低内存开销。常见的实现包括Apache Commons Pool、Netty的ByteBufAllocator等。

对于这三种内存分配方式,它们各自有不同的优势:

  • 直接分配的优势在于可以提供更高的IO性能,尤其是在处理大量数据时。它避免了数据从Java堆复制到操作系统内核缓冲区的过程,减少了内存拷贝的开销。
  • 非直接分配相对于直接分配来说,具有更低的分配和释放成本,并且在某些情况下可以更好地利用缓存。它适用于小规模数据的处理,且不需要频繁的IO操作。
  • 池化分配通过重复使用ByteBuffer实例来减少内存分配和垃圾回收的开销,适用于长时间运行的应用程序。

底层实现方面,Java的ByteBuffer类是通过JNI(Java Native Interface)与操作系统进行交互来实现的。在直接分配的情况下,ByteBuffer通过调用操作系统的相关API来分配和释放内存。在非直接分配的情况下,ByteBuffer使用Java堆中的字节数组来存储数据,并且提供了一系列的操作方法来访问和修改这些数据。对于池化分配,具体的实现方式可能会有所不同,但基本原理是通过管理ByteBuffer实例的生命周期来重复利用内存

池化ByteBuffer技术

netty-PooledByteBufAllocator

PooledByteBufAllocator是Netty中的一种内存分配器,用于管理ByteBuf的分配和释放。它使用池化技术来重复利用已经分配的ByteBuf,从而避免了频繁地创建和销毁对象,提高了性能。

PooledByteBufAllocator主要有两个池子:Heap Pool和Direct Pool。 Heap Pool用于分配JVM堆内存空间,Direct Pool则用于分配直接内存空间。

当需要分配ByteBuf时,PooledByteBufAllocator会先检查请求的大小是否超过了chunk(内存块)的大小,默认情况下是16MB。如果请求的大小小于等于chunk的大小,则会从相应的池子中寻找可用的资源进行分配;如果请求的大小超过了chunk的大小,则会直接使用JVM的内存分配器或者操作系统的malloc来分配内存。

在分配ByteBuf时,PooledByteBufAllocator会尽可能地重复利用已经回收的ByteBuf,从而避免了频繁地创建和销毁对象。当一个ByteBuf被释放时,它不会立即被销毁,而是会被加入到相应的Chunk中,等待下一次分配时再次被使用。这样可以减少GC的负担,提高系统性能。

另外需要注意的是,在使用PooledByteBufAllocator时,需要及时释放已经使用完毕的ByteBuf,否则会造成内存泄漏。可以使用ReferenceCountUtil.release()方法来释放ByteBuf,它会自动判断当前ByteBuf的引用计数,如果引用计数为0,则会将其归还给相应的池子。

总之,PooledByteBufAllocator是Netty中一种高效的内存分配器,通过池化技术和重复利用已经分配的ByteBuf,可以提高系统性能并减少GC的负担。

netty-UnpooledByteBufAllocator

UnpooledByteBufAllocator 是 Netty 中的一种 ByteBuf 分配器,与 PooledByteBufAllocator 和 PooledUnsafeDirectByteBuf 不同,它不使用池化技术,而是每次分配时都会创建一个新的 ByteBuf 对象。因此,UnpooledByteBufAllocator 不具备池化技术带来的性能优势,但却更加灵活,适用于一些特定的场景。

UnpooledByteBufAllocator 的特点如下:

  • 不使用池化技术,每次分配时都会创建一个新的 ByteBuf 对象。
  • 适用于一些需要灵活、快速分配 ByteBuf 的场景,例如测试、调试等。
  • 不需要进行内存块或者内存池的管理,减少了内存管理的复杂度和开销。
  • 由于不使用池化技术,可能会导致频繁地创建和销毁 ByteBuf 对象,增加 GC 的负担。

相比之下,PooledByteBufAllocator 和 PooledUnsafeDirectByteBuf 使用池化技术重复利用已经分配的 ByteBuf,避免频繁地创建和销毁对象,从而提高系统性能,并减轻 GC 的压力。同时,它们还可以根据应用程序的需求,灵活地配置 Chunk(内存块)的大小、ByteBuf 的最大容量、内存池的数量等参数,因此更适合于需要大量分配 ByteBuf 对象的高性能网络应用场景。

总之,UnpooledByteBufAllocator 不使用池化技术,适用于一些需要灵活、快速分配 ByteBuf 的场景。而 PooledByteBufAllocator 和 PooledUnsafeDirectByteBuf 使用池化技术重复利用已经分配的 ByteBuf,避免频繁地创建和销毁对象,更适合于需要大量分配 ByteBuf 对象的高性能网络应用场景。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

进窄门见微光行远路

如果对你有比较大的帮助

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

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

打赏作者

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

抵扣说明:

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

余额充值