Netty分布式高性能工具类异线程下回收对象解析

目录
  • 异线程回收对象
    • 跟到pushLater方法中
    • 跟到allocate方法中
    • 回到pushLater方法中
    • 简单看下link的类的定义
    • 回到pushLater方法中

前文传送门:Netty分布式高性能工具类同线程下回收对象解析

异线程回收对象

就是创建对象和回收对象不在同一条线程的情况下, 对象回收的逻辑

我们之前小节简单介绍过, 异线程回收对象, 是不会放在当前线程的stack中的, 而是放在一个WeakOrderQueue的数据结构中, 回顾我们之前的一个图:

8-6-1

相关的逻辑, 我们跟到源码中:

首先从回收对象的入口方法开始, DefualtHandle的recycle方法:

public void recycle(Object object) {
    if (object != value) {
        throw new IllegalArgumentException("object does not belong to handle");
    }
    stack.push(this);
}

这部分我们并不陌生, 跟到push方法中:

void push(DefaultHandle<?> item) {
    Thread currentThread = Thread.currentThread();
    if (thread == currentThread) {
        pushNow(item);
    } else {
        pushLater(item, currentThread);
    }
}

上一小节分析过, 同线程会走到pushNow, 有关具体逻辑也进行了分析

如果不是同线程, 则会走到pushLater方法, 传入handle对象和当前线程对象

跟到pushLater方法中

private void pushLater(DefaultHandle<?> item, Thread thread) {
    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;
        }
        if ((queue = WeakOrderQueue.allocate(this, thread)) == null) {
            return;
        }
        delayedRecycled.put(this, queue);
    } else if (queue == WeakOrderQueue.DUMMY) {
        return;
    }
    queue.add(item);
}

首先通过DELAYED_RECYCLED.get()获取一个delayedRecycled对象

我们跟到DELAYED_RECYCLED中:

private static final FastThreadLocal<Map<Stack<?>, WeakOrderQueue>> DELAYED_RECYCLED =
        new FastThreadLocal<Map<Stack<?>, WeakOrderQueue>>() {
    @Override
    protected Map<Stack<?>, WeakOrderQueue> initialValue() {
        return new WeakHashMap<Stack<?>, WeakOrderQueue>();
    }
};

这里我们看到DELAYED_RECYCLED是一个FastThreadLocal对象, initialValue方法创建一个WeakHashMap对象, WeakHashMap是一个map, key为stack, value为我们刚才提到过的WeakOrderQueue

从中我们可以分析到, 每个线程都维护了一个WeakHashMap对象

WeakHashMap中的元素, 是一个stack和WeakOrderQueue的映射, 说明了不同的stack, 对应不同的WeakOrderQueue

这里的映射关系可以举个例子说明:

比如线程1创建了一个对象, 在线程3进行了回收, 线程2创建了一个对象, 同样也在线程3进行了回收, 那么线程3对应的WeakHashMap中就会有两个元素:

线程1的stack和线程2的WeakOrderQueue, 线程2和stack和线程2的WeakOrderQueue

我们回到pushLater方法中:

继续往下看:

WeakOrderQueue queue = delayedRecycled.get(this)

拿到了当前线程的WeakHashMap对象delayedRecycled之后, 然后通过delayedRecycled创建对象的线程的stack, 拿到WeakOrderQueue

这里的this, 就是创建对象的那个线程所属的stack, 这个stack是绑定在handle中的, 创建handle对象时候进行的绑定

假设当前线程是线程2, 创建handle的线程是线程1, 这里通过handle的stack拿到线程1的WeakOrderQueue

if (queue == null) 说明线程2没有回收过线程1的对象, 则进入if块的逻辑:

首先看判断 if (delayedRecycled.size() >= maxDelayedQueues)

delayedRecycled.size() 表示当前线程回收其他创建对象的线程的线程个数, 也就是有几个其他的线程在当前线程回收对象

maxDelayedQueues表示最多能回收的线程个数, 这里如果朝超过这个值, 就表示当前线程不能在回收其他线程的对象了

通过 delayedRecycled.put(this, WeakOrderQueue.DUMMY) 标记, 创建对象的线程的stack, 所对应的WeakOrderQueue不可用, DUMMY我们可以理解为不可用

如果没有超过maxDelayedQueues, 则通过if判断中的 WeakOrderQueue.allocate(this, thread) 这种方式创建一个WeakOrderQueue

allocate传入this, 也就是创建对象的线程对应的stack, 假设是线程1, thread就是当前线程, 假设是线程2

跟到allocate方法中

static WeakOrderQueue allocate(Stack<?> stack, Thread thread) {
    return reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY)
            ? new WeakOrderQueue(stack, thread) : null;
}

reserveSpace(stack.availableSharedCapacity, LINK_CAPACITY)表示线程1的stack还能不能分配LINK_CAPACITY个元素, 如果可以, 则直接通过new的方式创建一个WeakOrderQueue对象

再跟到reserveSpace方法中:

private static boolean reserveSpace(AtomicInteger availableSharedCapacity, int space) {
    assert space >= 0;
    for (;;) {
        int available = availableSharedCapacity.get();
        if (available < space) {
            return false;
        }
        if (availableSharedCapacity.compareAndSet(available, available - space)) {
            return true;
        }
    }
}

参数availableSharedCapacity表示线程1的stack允许外部线程给其缓存多少个对象, 之前我们分析过是16384, space默认是16

方法中通过一个cas操作, 将16384减去16, 表示stack可以给其他线程缓存的对象数为16384-16

而这16个元素, 将由线程2缓存

回到pushLater方法中

创建之后通过 delayedRecycled.put(this, queue) 将stack和WeakOrderQueue进行关联

最后通过queue.add(item), 将创建的WeakOrderQueue添加一个handle

讲解WeakOrderQueue之前, 我们首先了解下WeakOrderQueue的数据结构

WeakOrderQueue维护了多个link, link之间是通过链表进行连接, 每个link可以盛放16个handle,

我们刚才分析过, 在reserveSpace方法中将 stack.availableSharedCapacity-16 , 其实就表示了先分配16个空间放在link里, 下次回收的时候, 如果这16空间没有填满, 则可以继续往里盛放

如果16个空间都已填满, 则通过继续添加link的方式继续分配16个空间用于盛放handle

WeakOrderQueue和WeakOrderQueue之间也是通过链表进行关联

可以根据下图理解上述逻辑:

8-6-2

根据以上思路, 我们跟到WeakOrderQueue的构造方法中:

private WeakOrderQueue(Stack<?> stack, Thread thread) {
    head = tail = new Link();
    owner = new WeakReference<Thread>(thread);
    synchronized (stack) {
        next = stack.head;
        stack.head = this;
    }
    availableSharedCapacity = stack.availableSharedCapacity;
}

这里有个head和tail, 都指向一个link对象, 这里我们可以分析到, 其实在WeakOrderQueue中维护了一个链表, head分别代表头结点和尾节点, 初始状态下, 头结点和尾节点都指向同一个节点

简单看下link的类的定义

private static final class Link extends AtomicInteger {
    private final DefaultHandle&lt;?&gt;[] elements = new DefaultHandle[LINK_CAPACITY];
    private int readIndex;
    private Link next;
}

每次创建一个Link, 都会创建一个DefaultHandle类型的数组用于盛放DefaultHandle对象, 默认大小是16个

readIndex是一个读指针, 我们之后小节会进行分析

next节点则指向下一个link

回到WeakOrderQueue的构造方法中:

owner是对向前线程进行一个包装, 代表了当前线程

接下来在一个同步块中, 将当前创建的WeakOrderQueue插入到stack指向的第一个WeakOrderQueue, 也就是stack的head属性, 指向我们创建的WeakOrderQueue, 如图所示

8-6-3

如果线程2创建一个和stack关联的WeakOrderQueue, stack的head节点就就会指向线程2创建WeakOrderQueue

如果之后线程3也创建了一个和stack关联的WeakOrderQueue, stack的head节点就会指向新创建的线程3的WeakOrderQueue

然后线程3的WeakOrderQueue再指向线程2的WeakOrderQueue

也就是无论哪个线程创建一个和同一个stack关联的WeakOrderQueue的时候, 都插入到stack指向的WeakOrderQueue列表的头部

这样就可以将stack和其他线程释放对象的容器WeakOrderQueue进行绑定

回到pushLater方法中

private void pushLater(DefaultHandle<?> item, Thread thread) {
    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;
        }
        if ((queue = WeakOrderQueue.allocate(this, thread)) == null) {
            return;
        }
        delayedRecycled.put(this, queue);
    } else if (queue == WeakOrderQueue.DUMMY) {
        return;
    }
    queue.add(item);
}

根据之前分析的WeakOrderQueue的数据结构, 我们分析最后一步, 也就是WeakOrderQueue的add方法

我们跟进WeakOrderQueue的add方法:

void add(DefaultHandle<?> handle) {
    handle.lastRecycledId = id;
    Link tail = this.tail;
    int writeIndex;
    if ((writeIndex = tail.get()) == LINK_CAPACITY) {
        if (!reserveSpace(availableSharedCapacity, LINK_CAPACITY)) {
            return;
        }
        this.tail = tail = tail.next = new Link();
        writeIndex = tail.get();
    }
    tail.elements[writeIndex] = handle;
    handle.stack = null;
    tail.lazySet(writeIndex + 1);
}

首先, 看 handle.lastRecycledId = id

lastRecycledId表示handle上次回收的id, 而id表示WeakOrderQueue的id, weakOrderQueue每次创建的时候, 会为自增一个唯一的id

Link tail = this.tail 表示拿到当前WeakOrderQueue的中指向最后一个link的指针, 也就是尾指针

再看 if ((writeIndex = tail.get()) == LINK_CAPACITY)

tail.get()表示获取当前link中已经填充元素的个数, 如果等于16, 说明元素已经填充满

然后通过eserveSpace方法判断当前WeakOrderQueue是否还能缓存stack的对象, eserveSpace方法我们刚才已经分析过, 会根据stack的属性availableSharedCapacity-16的方式判断还能否缓存stack的对象, 如果不能再缓存stack的对象, 则返回

如果还能继续缓存, 则在创建一个link, 并将尾节点指向新创建的link, 并且原来尾节点的next的节点指向新创建的link

然后拿到当前link的writeIndex, 也就是写指针, 如果是新创建的link中没有元素, writeIndex为0

之后将尾部的link的elements属性, 也就是一个DefaultHandle类型的数组, 通过数组下标的方式将第writeIndex个节点赋值为要回收的handle

然后将handle的stack属性设置为null, 表示当前handle不是通过stack进行回收的

最后将tail节点的元素个数进行+1, 表示下一次将从writeIndex+1的位置往里写

以上就是异线程回收对象的逻辑

以上就是Netty分布式高性能工具类异线程下回收对象解析的详细内容,更多关于Netty分布式异线程回收对象的资料请关注我们其它相关文章!

(0)

相关推荐

  • Netty分布式高性能工具类FastThreadLocal和Recycler分析

    目录 概述 第一节:FastThreadLocal的使用和创建 首先我们看一个最简单的demo 跟到nextVariableIndex方法中 我们首先剖析slowGet()方法 我们跟进fastGet 回到FastThreadLocal的get方法中 在我们的demo中对应这个方法 前文传送门:Netty分布式Future与Promise执行回调相关逻辑剖析 概述 FastThreadLocal我们在剖析堆外内存分配的时候简单介绍过, 它类似于JDK的ThreadLocal, 也是用于在多线程条

  • Netty分布式FastThreadLocal的set方法实现逻辑剖析

    目录 FastThreadLocal的set方法实现 线程set对象 我们跟到setIndexedVariable中 我们跟进removeIndexedVariable方法 上一小节我们学习了FastThreadLocal的创建和get方法的实现逻辑, 这一小节学习FastThreadLocal的set方法的实现逻辑 FastThreadLocal的set方法实现 set方法, 其实就是修改线程共享对象, 作用域只是当前线程, 我们回顾根据上一小节demo中, 其中一个线程set对象的过程: 线

  • Netty分布式从recycler对象回收站获取对象过程剖析

    前文传送门:Netty分布式高性能工具类recycler的使用及创建 从对象回收站中获取对象 我们回顾上一小节demo的main方法中 从回收站获取对象 public static void main(String[] args){ User user1 = RECYCLER.get(); user1.recycle(); User user2 = RECYCLER.get(); user2.recycle(); System.out.println(user1==user2); } 这个通过R

  • Netty分布式高性能工具类同线程下回收对象解析

    目录 同线程回收对象 回顾第三小节的demo中的main方法 我们跟进recycle方法 然后获取当前size 同线程回收对象 上一小节剖析了从recycler中获取一个对象, 这一小节分析在创建和回收是同线程的前提下, recycler是如何进行回收的 回顾第三小节的demo中的main方法 public static void main(String[] args){ User user1 = RECYCLER.get(); user1.recycle(); User user2 = REC

  • Netty分布式高性能工具类recycler的使用及创建

    目录 recycler的使用 这里看一个示例 在Recycler的类的源码中, 我们看到这一段逻辑 跟到Stack的构造方法中 继续跟重载的构造方法 我们再回到Stack的构造方法中 前文传送门:Netty分布式FastThreadLocal的set方法实现逻辑剖析 recycler的使用 这一小节开始学习recycler相关的知识, recycler是netty实现的一个轻量级对象回收站, 在netty中, recycler的使用也是相当之频繁的 recycler作用是保证了对象的循环利用, 

  • Netty分布式Future与Promise执行回调相关逻辑剖析

    目录 Future和Promise执行回调 首先我们看一段写在handler中的业务代码 这里关注newPromise()方法, 跟进去 我们继续跟write方法 跟进tryFailure方法 跟到addMessage方法中 最后跟到AbstractUnsafe的flush方法 我们跟到remove()方法中 再跟到trySuccess方法中 我们看用户代码 跟到addListener0方法中 回到addListener0方法中 跟到isDone方法中 跟到notifyListeners()方法

  • Netty分布式高性能工具类异线程下回收对象解析

    目录 异线程回收对象 跟到pushLater方法中 跟到allocate方法中 回到pushLater方法中 简单看下link的类的定义 回到pushLater方法中 前文传送门:Netty分布式高性能工具类同线程下回收对象解析 异线程回收对象 就是创建对象和回收对象不在同一条线程的情况下, 对象回收的逻辑 我们之前小节简单介绍过, 异线程回收对象, 是不会放在当前线程的stack中的, 而是放在一个WeakOrderQueue的数据结构中, 回顾我们之前的一个图: 8-6-1 相关的逻辑, 我

  • Netty分布式ByteBuf使用的底层实现方式源码解析

    目录 概述 AbstractByteBuf属性和构造方法 首先看这个类的属性和构造方法 我们看几个最简单的方法 我们重点关注第二个校验方法ensureWritable(length) 我们跟到扩容的方法里面去 最后将写指针后移length个字节 概述 熟悉Nio的小伙伴应该对jdk底层byteBuffer不会陌生, 也就是字节缓冲区, 主要用于对网络底层io进行读写, 当channel中有数据时, 将channel中的数据读取到字节缓冲区, 当要往对方写数据的时候, 将字节缓冲区的数据写到cha

  • Java Atomic类及线程同步新机制原理解析

    一.为什么要使用Atomic类? 看一下下面这个小程序,模拟计数,创建10个线程,共同访问这个int count = 0 :每个线程给count往上加10000,这个时候你需要加锁,如果不加锁会出现线程安全问题,但是使用AtomicInteger之后就不用再做加锁的操作了,因为AtomicInteger内部使用了CAS操作,直接无锁往上递增,有人会问问什么会出现无锁操作,答案只有一个:那就是快呗: 下面是AtomicInteger的使用方法: package com.example.demo.t

  • Netty分布式获取异线程释放对象源码剖析

    目录 获取异线程释放对象 在介绍之前我们首先看Stack类中的两个属性 我们跟到pop方法中 继续跟到scavengeSome方法中 我们继续分析transfer方法 接着我们我们关注一个细节 我们跟到reclaimSpace方法 章节小结 前文传送门:异线程下回收对象 获取异线程释放对象 上一小节分析了异线程回收对象, 原理是通过与stack关联的WeakOrderQueue进行回收 如果对象经过异线程回收之后, 当前线程需要取出对象进行二次利用, 如果当前stack中为空, 则会通过当前st

  • 19个Android常用工具类汇总

    主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java. 目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils.PreferencesUtils.JSONUtils.FileUtils.ResourceUtils.StringUtils.ParcelUtils.RandomUtils.ArrayUtils.ImageUtils.ListUtils.MapUtils.ObjectUtils.SerializeUtils.

  • Java并发工具类Exchanger的相关知识总结

    一.Exchanger的理解 Exchanger 属于java.util.concurrent包: Exchanger 是 JDK 1.5 开始提供的一个用于两个工作线程之间交换数据的封装工具类; 一个线程在完成一定的事务后想与另一个线程交换数据,则第一个先拿出数据的线程会一直等待第二个线程,直到第二个线程拿着数据到来时才能彼此交换对应数据. 二.Exchanger类中常用方法 public Exchanger() 无参构造方法.表示创建一个新的交换器. public V exchange(V

随机推荐