[Netty源码] Netty轻量级对象池实现分析 (十三)

news/2024/5/21 1:56:44

文章目录

      • 1.对象池技术介绍
      • 2.如何实现对象池
      • 3.Netty对象池实现分析
        • 3.1 Recycler
        • 3.2 Handler
        • 3.3 Stack
        • 3.4 WeakOrderQueue
        • 3.5 Link
      • 4.总结

1.对象池技术介绍

对象池其实就是缓存一些对象从而避免大量创建同一个类型的对象, 类似线程池。对象池缓存了一些已经创建好的对象, 避免需要的时候创建。同时限制了实例的个数。

池化技术最终要的就是重复的使用池内已经创建的对象。

  • 创建对象的开销大
  • 会创建大量的实例
  • 限制一些资源的使用

如果创建一个对象的开销特别大, 那么需要提前创建一些可以使用的并缓存起来, 池化技术就是重复使用对象, 提前创建并缓存起来重复使用就是池化, 可以降低创建对象的开销。

会大量创建实例的场景, 重复的使用对象可以减少创建的对象数量, 降低GC的压力。

对于限制资源的使用更多的是一种保护机制, 比如数据库连接池, 除去创建对象本身的开销, 们对外部系统也会造成压力, 比如大量创建链接对DB也是有压力的。那么池化除了可以优化资源外, 本身限制了资源数, 对外部系统也起到了一层保护作用。

2.如何实现对象池

Apache Commons Pool, Netty轻量级对象池的实现。

Apache Commons Pool开源软件库提供了一个对象池API和一系列对象池的实现, 支持各种配置, 比如活跃对象数或者闲置对象个数等。DBCP数据库连接池基于Apache Commons Pool实现。

Netty自己实现了一套轻量级的对象池, 在多个IO线程独立工作时候, 基于NioEventLoop的实现, 每个IO线程轮询单独的Selector实例来检索IO事件, 在IO来临时候开始处理。最常见的IO操作就是读写, 具体到NIO就是从内核缓冲区拷贝数据到用户缓冲区或者从用户缓冲区拷贝数据到内核缓冲区。

3.Netty对象池实现分析

NIO提供了两种Buffer最为缓冲区: DirectByteBuffer和HeapByteBuffer, Netty进行了池化提供性能。

Netty的池化分别为PooledDirectByteBuf 和PolledHeapByteBuf。

static PooledDirectByteBuf newInstance(int maxCapacity) {PooledDirectByteBuf buf = RECYCLER.get();buf.reuse(maxCapacity);return buf;}

可见RECYCLER是池化的核心, 通过RECYCLER.get获取一个实例。

Recycler就是Netty实轻量级池化技术的核心。

public class RecycleTest {private static final Recycler<User> RECYCLER = new Recycler<User>(){@Overrideprotected User newObject(Handle<User> handle) {return new User(handle);}};public static class User{private final Recycler.Handle<User> handle;public User(Recycler.Handle<User> handle){this.handle = handle;}public void recycle(){handle.recycle(this);}}public static void main(String[] args) {// 1.获取当前线程的stack// 2.从stack中弹出对象// 3.创建对象并绑定到stackUser user = RECYCLER.get();user.recycle();User user1 = RECYCLER.get();System.out.println(user == user1);}
}
true

3.1 Recycler

整个对象池的核心实现由ThreadLocal, Handler和Stack及WrakOrderQueue构成。

在这里插入图片描述

在这里插入图片描述

  1. Stack相当于是一级缓存,同一个线程内的使用和回收都将使用一个Stack
  2. 每个线程都会有一个自己对应的Stack,如果回收的线程不是Stack的线程,将元素放入到Queue中
  3. 所有的Queue组合成一个链表,Stack可以从这些链表中回收元素(实现了多线程之间共享回收的实例)

在这里插入图片描述

  • get(): 获取对象
    public final T get() {if (maxCapacityPerThread == 0) {return newObject((Handle<T>) NOOP_HANDLE);}Stack<T> stack = threadLocal.get();DefaultHandle<T> handle = stack.pop();if (handle == null) {handle = stack.newHandle();handle.value = newObject(handle);}return (T) handle.value;}
  • 拿到当前线程对应的stack
  • 从stack中pop出一个元素
  • 如果不为空则返回,否则创建一个新的实例
    public final boolean recycle(T o, Handle<T> handle) {if (handle == NOOP_HANDLE) {return false;}DefaultHandle<T> h = (DefaultHandle<T>) handle;if (h.stack.parent != this) {return false;}h.recycle(o);return true;}
  • Recycler的recycle方法主要做了一些参数验证。

3.2 Handler

recycle(): 放回池子中的方法, Recycler的recycle方法和DefaultHandle的recycle方法。

        @Overridepublic void recycle(Object object) {if (object != value) {throw new IllegalArgumentException("object does not belong to handle");}Stack<?> stack = this.stack;if (lastRecycledId != recycleId || stack == null) {throw new IllegalStateException("recycled already");}stack.push(this);}
  • Recycler的recycle方法主要做了一些参数验证。
  • DefaultHandle的recycle方法:
    1. 如果当前线程是当前stack对象的线程, 放入stack。
    2. 获取当前线程对应的Map<Stack, WeakOrderQueue>, 实例放入stack对应的queue中。

3.3 Stack

Stack是对象池化背后存储实例的数据结构, 如果能从stack中拿到可用的实例就不再创建新的实例。

        final Recycler<T> parent;final WeakReference<Thread> threadRef;final AtomicInteger availableSharedCapacity;final int maxDelayedQueues;private final int maxCapacity;private final int ratioMask;private DefaultHandle<?>[] elements;private int size;private int handleRecycleCount = -1; // Start with -1 so the first one will be recycled.private WeakOrderQueue cursor, prev;private volatile WeakOrderQueue head;

在这里插入图片描述

pop()

        DefaultHandle<T> pop() {int size = this.size;// 如过size = 0, scavengeif (size == 0) {if (!scavenge()) {return null;}size = this.size;}size --;DefaultHandle ret = elements[size];elements[size] = null;if (ret.lastRecycledId != ret.recycleId) {throw new IllegalStateException("recycled multiple times");}ret.recycleId = 0;ret.lastRecycledId = 0;this.size = size;// 返回elements最后一个元素return ret;}

push(): 同线程和异步线程回收对象

在这里插入图片描述

同步线程回收对象

        private void pushNow(DefaultHandle<?> item) {if ((item.recycleId | item.lastRecycledId) != 0) {throw new IllegalStateException("recycled already");}item.recycleId = item.lastRecycledId = OWN_THREAD_ID;int size = this.size;if (size >= maxCapacity || dropHandle(item)) {return;}if (size == elements.length) {elements = Arrays.copyOf(elements, min(size << 1, maxCapacity));}elements[size] = item;this.size = size + 1;}

异步线程回收对象

  1. 获取WeakOrderQueue
  2. 创建WeakOrderQueue
  3. 将对象追加到WeakOrderQueue
        private void pushLater(DefaultHandle<?> item, Thread thread) {// 获取WeakOrderQueueMap<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();WeakOrderQueue queue = delayedRecycled.get(this);if (queue == null) {if (delayedRecycled.size() >= maxDelayedQueues) {delayedRecycled.put(this, WeakOrderQueue.DUMMY);return;}// 创建WeakOrderQueueif ((queue = WeakOrderQueue.allocate(this, thread)) == null) {// drop objectreturn;}delayedRecycled.put(this, queue);} else if (queue == WeakOrderQueue.DUMMY) {// drop objectreturn;}// 将对象追加到WeakOrderQueuequeue.add(item);}

在这里插入图片描述

queue是一个队列形式, 节点为Link, Link中是Handler (处理逻辑)

3.4 WeakOrderQueue

  head,tail:Link                       // 内部元素的指针(WeakOrderQueue内部存储的是一个Link的链表)next:WeakOrderQueue     // 指向下一个WeakOrderQueue的指针owner:Thread                      // 对应的线程

在这里插入图片描述

WeakOrderQueue的结构图

在这里插入图片描述

WeakOrderQueue中是一个一个Link组成

在这里插入图片描述

add方法将元素添加到自身的“队列”中, transfer方法将自己拥有的元素“传输”到Stack中。

        void add(DefaultHandle<?> handle) {handle.lastRecycledId = id;Link tail = this.tail;int writeIndex;if ((writeIndex = tail.get()) == LINK_CAPACITY) {if (!head.reserveSpace(LINK_CAPACITY)) {// Drop it.return;}// We allocate a Link so reserve the spacethis.tail = tail = tail.next = new Link();writeIndex = tail.get();}tail.elements[writeIndex] = handle;handle.stack = null;// we lazy set to ensure that setting stack to null appears before we unnull it in the owning thread;// this also means we guarantee visibility of an element in the queue if we see the index updatedtail.lazySet(writeIndex + 1);}

add操作将元素添加到tail指向的Link对象中,如果Link已满则创建一个新的Link实例。

3.5 Link

        static final class Link extends AtomicInteger {// 处理逻辑Handlerprivate final DefaultHandle<?>[] elements = new DefaultHandle[LINK_CAPACITY];// 读的指针private int readIndex;Link next;}

Link内部包含了一个数组用于存放实例, 同时标记了读取位置的索引和下一个Link元素的指针。

在这里插入图片描述

4.总结

Recyler -> Stack-> WeakOrderQueue -> Link -> Handler

在这里插入图片描述


http://wed.xjx100/news/89830.html

相关文章

JAVAWeb05-xml、DOM4J

1. xml概述 1.1 官方文档 地址: https://www.w3school.com.cn/xml/index.asp 1.2 为什么需要 XML 需求 1 : 两个程序间进行数据通信&#xff1f;需求 2 : 给一台服务器&#xff0c;做一个配置文件&#xff0c;当服务器程序启动时&#xff0c;去读取它应当监听的端口号、还有…

图元操作(理论)

图元操作理论知识 Graphics View框架结构的主要特点 在Graphics View框架结构中,系统可以利用Qt绘图系统的反锯齿、OpenGL工具来改善绘图性能。Graphics View支持事件传播体系结构,可以使图元在场景(scene)中的交互能力提高1倍,图元能够处理键盘事件和鼠标事件。其中,鼠…

PostgreSQL (四) 索引

1.优点 创建唯一索引,保证数据的唯一性加快数据的查询速度建立索引可以加快表与表之间的连接为用来排序或者是分组的字段添加索引可以加快分组和排序顺序 2.原则 序号原则1选择唯一性索引2为经常需要排序、分组和联合操作的字段建立索引3为常作为查询条件的字段建立索引4限制…

Filter 过滤器 Listener 监听器

Filter web中的过滤器当用户访问服务器资源时&#xff0c;过滤器将请求拦截下来&#xff0c;完成一些通用的操作应用场景如&#xff1a;登录验证、统一编码处理、敏感字符过滤 编写filter对目标资源servlet进行拦截 1. 编写java类&#xff0c;实现filter接口 public class Qu…

BGP路由优选实验

一&#xff0c;实验要求及其拓扑图 二&#xff1a;划分好IP的拓扑 三&#xff1a; 实验分析 1、使用 Preval 策略&#xff0c;确保R4通过R2到达192.168.10.0/24 1、抓取流量 [r4]ip ip-prefix PV permit 192.168.10.0 24 2、配置策略 [r4]route-policy PV permit node 10 [r4…

【LeetCode: 337. 打家劫舍 III | 暴力递归=>记忆化搜索=>动态规划 | 树形dp】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

规模化敏捷框架:Spotify

Spotify 是全球最大、最受欢迎的流媒体音乐服务平台&#xff0c;预估用户总量已达2.86亿。Spotify 取得成功的一个关键因素就在于公司采用了一个独特方法: 围绕工作任务进行组织构建以提高团队敏捷性。Spotify 工程团队把提高团队敏捷性的经验记录了下来&#xff0c;并把经验分…

【华为OD机试真题】字符串解密(javaC++python)100%通过率

字符串解密 知识点数组字符串排序 时间限制:1s空间限制:256MB限定语言:不限 题目描述: 给定两个字符串string1和string2。 string1是一个被加扰的字符串。string1由小写英文字母(‘a’~‘z’)和数字字符 (‘0’~ ‘9’)组成,而加扰字符串由’0’~ ‘9’、‘a’~f’…