Netty
的ByteBuf
有从不同角度有如下2个分类,4种组合!
我们在利用Netty
做底层通信框架的时候,会默认给我们的到底是哪一种组合了?
Netty的Boostrap
启动类按照标准模板,通常会添加这个配置option(ChannelOption.ALLOCATOR, ByteBufAllocator.DEFAULT)
val serverBootstrap = ServerBootstrap().group(bossGroup, workGroup)
.channel(NioServerSocketChannel::class.java) //配置客户端的channel类型
.option(ChannelOption.ALLOCATOR, ByteBufAllocator.DEFAULT)
//.....
那么通过默认的ByteBufAllocator.DEFAULT
分配出来的是哪一种组合了?我们来通过代码分析。
首先看看这个ByteBufAllocator.DEFAULT
创建了一个什么类
public interface ByteBufAllocator {
ByteBufAllocator DEFAULT = ByteBufUtil.DEFAULT_ALLOCATOR;
}
继续调用下去,下述代码省略了一些无关的细节!
public final class ByteBufUtil {
static final ByteBufAllocator DEFAULT_ALLOCATOR;
static {
//核心语句
String allocType = SystemPropertyUtil.get(
"io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled");
allocType = allocType.toLowerCase(Locale.US).trim();
ByteBufAllocator alloc;
if ("unpooled".equals(allocType)) {
alloc = UnpooledByteBufAllocator.DEFAULT;
} else if ("pooled".equals(allocType)) {
alloc = PooledByteBufAllocator.DEFAULT;
} else {
alloc = PooledByteBufAllocator.DEFAULT;
}
//这里赋值了噢...
DEFAULT_ALLOCATOR = alloc;
}
上述代码最为核心的一句是:
String allocType = SystemPropertyUtil.get(
"io.netty.allocator.type", PlatformDependent.isAndroid() ? "unpooled" : "pooled")
其表达的意思是
系统配置(通常是启动的命令行给的)
中读取io.netty.allocator.type
,如果能够读取到,那么按照配置设置。但是这个参数我们通常是没有设置的,所以会进行第二步判定PlatformDependent.isAndroid()
,当Netty
运行在Android
的时候是unpooled
,否则是pooled
后面的代码就是根据pooled
和unpooled
来进行对象初始化了。
综上,我们大多数时候启动参数都是没有加对应配置的,且运行在非安卓的系统,所以是pooled
的ByteBuf
接下来还需要确定默认情况下分配的ByteBuf
是分配到堆内还是堆外的,从PooledByteBufAllocator.DEFAULT;
点进去可以看到这个代码
public static final PooledByteBufAllocator DEFAULT =
new PooledByteBufAllocator(PlatformDependent.directBufferPreferred());
这个构造函数的参数directBufferPreferred
的中文意思不就是偏向堆外内存
吗?不过按照传统这个应该是一个boolean
的变量,猜测了这些,我们继续看经过省略的源码PlatformDependent
。
public final class PlatformDependent {
private static final boolean DIRECT_BUFFER_PREFERRED;
static {
if (!isAndroid()) {
if (javaVersion() >= 9) {
CLEANER = CleanerJava9.isSupported() ? new CleanerJava9() : NOOP;
} else {
CLEANER = CleanerJava6.isSupported() ? new CleanerJava6() : NOOP;
}
} else {
CLEANER = NOOP;
}
DIRECT_BUFFER_PREFERRED = CLEANER != NOOP && !SystemPropertyUtil.getBoolean("io.netty.noPreferDirect", false);
}
核心的参数DIRECT_BUFFER_PREFERRED
要想为true
必须经过2步:
CLEANER
,这个判定牵扯到了另外的判定,这里就不贴代码了,意思应该就是有没有堆外内存
的清理办法,Java7+应该都有的io.netty.noPreferDirect
是不是为false
通常Java后端的系统这2个条件都是满足的,所以恭喜:DIRECT_BUFFER_PREFERRED
为true
,堆外内存。
通过上面分析,我们知道了Netty
默认的ByteBuf是堆外的池化模式
,那么池化与否
和堆外与否
各自的优劣在哪里!?
通常池化意味者可以复用资源,减少系统资源的开销
堆外内存可以使用更多可用的空间,毕竟Java虚拟机的大小一般都要小于系统的大小。但是江湖传言堆外内存的开辟比堆内内存的开辟慢,这一点我不太能理解,这里记录下。
以下是我在发文后的第二天从java.nio.ByteBuffer
看到的文档注释!
A direct byte buffer may be created by invoking the
{@link #allocateDirect(int) allocateDirect}
factory method of this class. The buffers returned by this method typically have somewhat higher allocation and deallocation costs than non-direct buffers.
上文的意思翻译下就是
堆外内存的创建可以通过调用
allocateDirect
这个工厂方法来创建。通过这个方法创建的buffer通常来说会比非堆外内存
有更高的创建和释放成本。
这里我还是纳闷啊!**不是说C
的效率要高于Java
吗?为什么堆外内存的创建和释放会比堆内内存的成本更高了?**有知道的大佬吗?
本篇文章主要通过启动参数的配置,进行了源码分析,确认了Netty的ByteBuf使用的是堆外的池外模式
。