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

news2024/10/6 4:14:21

文章目录

      • 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>(){
        @Override
        protected 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.创建对象并绑定到stack
        User 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方法。

        @Override
        public 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, scavenge
            if (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) {
			// 获取WeakOrderQueue
            Map<Stack<?>, WeakOrderQueue> delayedRecycled = DELAYED_RECYCLED.get();
            WeakOrderQueue queue = delayedRecycled.get(this);
            if (queue == null) {
                if (delayedRecycled.size() >= maxDelayedQueues) {
                    delayedRecycled.put(this, WeakOrderQueue.DUMMY);
                    return;
                }
                // 创建WeakOrderQueue
                if ((queue = WeakOrderQueue.allocate(this, thread)) == null) {
                    // drop object
                    return;
                }
                delayedRecycled.put(this, queue);
            } else if (queue == WeakOrderQueue.DUMMY) {
                // drop object
                return;
            }
			// 将对象追加到WeakOrderQueue
            queue.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 space
                this.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 updated
            tail.lazySet(writeIndex + 1);
        }

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

3.5 Link

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

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

在这里插入图片描述

4.总结

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/418407.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uni-app--》什么是uniapp?如何开发uniapp?

&#x1f3cd;️作者简介&#xff1a;大家好&#xff0c;我是亦世凡华、渴望知识储备自己的一名在校大学生 &#x1f6f5;个人主页&#xff1a;亦世凡华、 &#x1f6fa;系列专栏&#xff1a;uni-app &#x1f6b2;座右铭&#xff1a;人生亦可燃烧&#xff0c;亦可腐败&#xf…

企业电子招投标采购系统源码——功能模块功能描述+数字化采购管理 采购招投标

​ 功能模块&#xff1a; 待办消息&#xff0c;招标公告&#xff0c;中标公告&#xff0c;信息发布 描述&#xff1a; 全过程数字化采购管理&#xff0c;打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力&#xff0c;为外…

HTTP API接口设计规范

1. 所有请求使用POST方法 使用post&#xff0c;相对于get的query string&#xff0c;可以支持复杂类型的请求参数。例如日常项目中碰到get请求参数为数组类型的情况。 便于对请求和响应统一做签名、加密、日志等处理 2. URL规则 URL中只能含有英文&#xff0c;使用英文单词或…

Docker配置DL envs教程

Docker容器与镜像的区别 Docker镜像类似于虚拟镜像&#xff0c;是一个只读的文件&#xff0c;包括进程需要运行所需要的可执行文件、依赖软件、库文件、配置文件等等。 而容器则是基于镜像创建的进程&#xff0c;可以利用容器来运行应用。 总结来说&#xff0c;镜像只读&#…

贾俊平《统计学》第七章知识点总结及课后习题答案

一.考点归纳 参数估计的基本原理 1置信区间 &#xff08;1&#xff09;置信水平为95%的置信区间的含义&#xff1a;用某种方法构造的所有区间中有95%的区间包含总体参数的真值。&#xff08;2&#xff09;置信度愈高&#xff08;即估计的可靠性愈高&#xff09;&#xff0c;则…

ABeam News | ABeam Consulting 荣获『SAP AWARD OF EXCELLENCE 2023』奖项

ABeam Consulting株式会社&#xff08;总裁兼CEO 鸭居 达哉、东京都千代田区、以下简称为ABeam Consulting&#xff09;在SAP 日本株式会社&#xff08;董事长 铃木洋史、东京都千代田区、以下简称为SAP日本&#xff09;表彰优秀合作伙伴的颁奖『SAP AWARD OF EXCELLENCE 2023』…

c3p0报错java.lang.NoClassDefFoundError: com/mchange/v2/ser/Indirector

1. 问题由来 今天第一次学习到c3p0的时候&#xff0c;学习资料上使用的是0.9.1.2版本。 我偷懒使用的是0.9.2版本。但是运行的时候会报错&#xff1a; 网上搜索了一下这个错误&#xff0c;很多人说去安装mchange-commons-0.2.jar 这个包 但是我看学习资料上没有去另外安装这…

nodejs+vue 图书借阅管理系统

该系统的应用可以减少工作人员的劳动强度&#xff0c;提高工作效率与管理水平&#xff0c;具有很大的价值。它可以使图书这项借阅业务操作简单&#xff0c;成功率高&#xff0c;使网上图书管理系统的管理工作向一个新的层次迈进。本论文是以构建图书借阅为目标&#xff0c;使用…

《100天精通Python丨从快速入门到黑科技》 >>> 目录导航

文章目录一、100 天精通 Python 丨基础知识篇100 天精通 Python 丨基础知识篇 —— 01、C 站最全 Python 标准库总结100 天精通 Python 丨基础知识篇 —— 02、Python 和 Pycharm&#xff08;语言特点、学习方法、工具安装&#xff09;100 天精通 Python 丨基础知识篇 —— 03、…

基于AIGC的3D场景创作引擎概述

通过改变3D场景制作流程复杂、成本高、门槛高、流动性差的现状&#xff0c;让商家像玩转2D一样去玩转3D&#xff0c;让普通消费者也能参与到3D内容创作和消费中&#xff0c;真正实现内容生产模式从PGC/UGC过渡到AIGC&#xff0c;是我们3D场景智能创作引擎一直追求的目标。前言随…

【VMD-SSA-LSSVM】基于变分模态分解与麻雀优化Lssvm的负荷预测【多变量】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【C++基础】引用(引用的概念;引用的特性;常引用;使用场景:做输出型参数、大对象传参、做输出型返回值、返回大对象的引用);引用和指针的区别)

六、引用 6.1 引用的概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。&#xff08;语法上&#xff09; 格式&#xff1a;类型& 引用变量名(对象名) …

矮床酸阻滞技术及其在酸回收行业的应用

废酸是如何产生的&#xff1f; 为什么要进行废酸回收&#xff1f; 在轧钢厂在轧制过程中&#xff0c;铁与氧或铁与燃料燃烧时的生成物(CO2、H2O等)的化学作用&#xff0c;而形成一层氧化层&#xff0c;称为氧化皮。其实我们日常生活中接触到的金属件&#xff0c;尤其是铁件上的…

mmsegmentation 训练自己的数据集

一. MMSegmentation是什么&#xff1f; MMSegmentation 是一个基于 PyTorch 的语义分割开源工具箱&#xff0c;它是 OpenMMLab 项目的一部分。他与MMDetection类似&#xff0c;集成了各种语义分割算法&#xff0c;可以快速验证语义分割效果。 二. 环境准备 参考&#xff1a…

【C++】模版(一)

泛型编程、模版&#xff08;一&#xff09;&#xff1a; 1.泛型编程&#xff1a; void Swap(int& left, int& right) {int temp left;left right;right temp; } void Swap(double& left, double& right) {double temp left;left right;right temp; }【…

电脑软件:office文档密码了怎么办,看完你就能自己找回了

Word、Excel、PPT作为大家日常办公的使用最频繁的办公软件。对于一些涉及个人隐私或者涉及企业机密性的文档文件&#xff0c;我们经常会给自己的文档加上密码。 但有时候文档时间比较久了、或者密码太多了&#xff0c;有时候我们会把文档的密码给忘记了&#xff0c;如果密码忘…

Shell练习

一、题目 1、编写函数&#xff0c;实现打印绿色OK和红色FAILED 判断是否有参数&#xff0c;存在为Ok&#xff0c;不存在为FAILED 2、编写函数&#xff0c;实现判断是否无位置参数&#xff0c;如无参数&#xff0c;提示错误 3、编写函数实现两个数字做为参数&#xff0c;返回最…

【大数据之Hadoop】十一、MapReduce之Shuffle、MapTask、ReduceTask工作机制

1 Shuffle机制 对于排序而言分为两个阶段&#xff0c;MapTask后和ReduceTask前。 2 MapTask工作机制 MapTask并行度由切片个数决定&#xff1b;切片个数由切片大小&#xff08;切片大小取决于块大小、maxsize&#xff08;Long的最大值&#xff09;和minsize&#xff08;默认为…

【华为机试真题详解JAVA实现】—字符串通配符

目录 一、题目描述 二、解题代码 一、题目描述 问题描述:在计算机中,通配符一种特殊语法,广泛应用于文件搜索、数据库、正则表达式等领域。现要求各位实现字符串通配符的算法。 要求: 实现如下2个通配符: *:匹配0个或以上的字符(注:能被*和?匹配的字符仅由英文字母和…

为一副通用纸牌设计数据结构

为一副通用纸牌设计数据结构 大家好&#xff0c;我是易安&#xff0c;今天我们来聊一道笔试题&#xff0c;这也是我曾经面试华为时做过的题&#xff0c;今天分享给大家。 题目&#xff1a; 如何设计一个通用的扑克牌数据结构&#xff1f;请解释如何继承它来实现特定的扑克游戏…