kafka producer BufferPool

Insert picture description here

allocate

If the size of the batch we applied for this time is equal to the size of a batch we set, and our memory pool is not empty, then we can use it directly by obtaining a block of memory from the memory pool.
Another situation is that we have 10k of memory left in our entire memory pool, but the memory we applied for this time is 32k, and the batch may be 16k, but one of our messages
is 32K -> max(16,32) = current Batch = 32K
may not be able to allocate such a large amount of memory at once, but you can allocate a little bit first.
If the size of the allocated memory is still not as large as the amount of memory to be applied for. The memory pool will always allocate memory and allocate it bit by bit. Waiting for others will release the memory.
moreMemory.await

ByteBuffer buffer = free.allocate(size, maxTimeToBlock);
    public ByteBuffer allocate(int size, long maxTimeToBlockMs) throws InterruptedException {
    
    
        //如果你想要申请的内存的大小,如果超过32M
        if (size > this.totalMemory)
            throw new IllegalArgumentException("Attempt to allocate " + size
                                               + " bytes, but there is a hard limit of "
                                               + this.totalMemory
                                               + " on memory allocations.");
        //加锁的代码
        this.lock.lock();
        try {
    
    
            // check if we have a free buffer of the right size pooled
            //poolableSize 代表的是一个批次的大小,默认情况一下,一个批次的大小是16K。
            //如果我们这次申请的批次的大小等于 我们设定好的一个批次的大小
            //并且我们的内存池不为空,那么直接从内存池里面获取一个块内存就可以使用了。
            //跟我们连接池是一个道理。

            //我们场景驱动的方式,第一次进来
            //内存池里面里面是没有内存的,所以这儿是获取不到内存。

            //poolable  Size默认批次的大小
            //申请的这个内存的大小是否等于默认的批次的内存大小
            //
            if (size == poolableSize && !this.free.isEmpty())
                return this.free.pollFirst();

            // now check if the request is immediately satisfiable with the
            // memory on hand or if we need to block
            //内存的个数 *  批次的大小 = free的大小
            //内存池内存的大小
            int freeListSize = this.free.size() * this.poolableSize;
            // size: 我们这次要申请的内存
            //this.availableMemory + freeListSize 目前可用的总内存
            //this.availableMemory + freeListSize 目前可用的总内存 大于你要申请的内存。
            if (this.availableMemory + freeListSize >= size) {
    
    
                // we have enough unallocated or pooled memory to immediately
                // satisfy the request
                freeUp(size);
                //进行内存扣减
                this.availableMemory -= size;
                lock.unlock();
                //直接分配内存
                return ByteBuffer.allocate(size);//16k
            } else {
    
    
                //还有一种情况就是,我们整个内存池 还剩10k的内存,但是我们这次申请的内存是32k
                //批次可能就是16k,但是我们的一条消息,就是32K -> max(16,32) = 当前批次 = 32K

                // we are out of memory and will have to block
                //统计分配的内存
                int accumulated = 0;
                ByteBuffer buffer = null;
                //
                Condition moreMemory = this.lock.newCondition();
                long remainingTimeToBlockNs = TimeUnit.MILLISECONDS.toNanos(maxTimeToBlockMs);
                //等待 别人释放内存
                this.waiters.addLast(moreMemory);
                // loop over and over until we have a buffer or have reserved
                // enough memory to allocate one

                /**
                 * 总的分配的思路,可能一下子分配不了这么大的内存,但是可以先有点分配一点。
                 *
                 * 如果分配的内存的大小 还是没有要申请的内存大小大。
                 * 内存池就会一直分配的内存,一点一点的去分配。
                 * 等着别人会释放内存。
                 */
                //accumulated 5K+16K=21K 16K
                // size 32K
                while (accumulated < size) {
    
    
                    long startWaitNs = time.nanoseconds();
                    long timeNs;
                    boolean waitingTimeElapsed;
                    try {
    
    
                        //在等待,等待别人释放内存。
                        //如果这儿的代码是等待wait操作
                        //那么我们可以猜想一下,当有人释放内存的时候
                        //肯定不是得唤醒这儿的代码

                        //目前代码一直在等待着
                        //假设,突然有人 往内存池里面还了内存。
                        //那么这儿的代码就可以被唤醒了。代码就继续往下执行。

                        // (1) 时间到了
                        // (2) 被人唤醒了。
                        waitingTimeElapsed = !moreMemory.await(remainingTimeToBlockNs, TimeUnit.NANOSECONDS);
                    } catch (InterruptedException e) {
    
    
                        this.waiters.remove(moreMemory);
                        throw e;
                    } finally {
    
    
                        long endWaitNs = time.nanoseconds();
                        timeNs = Math.max(0L, endWaitNs - startWaitNs);
                        this.waitTime.record(timeNs, time.milliseconds());
                    }

                    if (waitingTimeElapsed) {
    
    
                        this.waiters.remove(moreMemory);
                        throw new TimeoutException("Failed to allocate memory within the configured max blocking time " + maxTimeToBlockMs + " ms.");
                    }

                    remainingTimeToBlockNs -= timeNs;
                    // check if we can satisfy this request from the free list,
                    // otherwise allocate memory
                    //再次尝试看一下,内存池里面有没有数据了。
                    //如果内存池里面有数据
                    //并且你申请的内存的大小就是一个批次的大小
                    //32K 16K
                    if (accumulated == 0 && size == this.poolableSize && !this.free.isEmpty()) {
    
    
                        // just grab a buffer from the free list
                        //这儿就可以直接获取到内存。
                        buffer = this.free.pollFirst();
                        accumulated = size;
                    } else {
    
    
                        // we'll need to allocate memory, but we may only get
                        // part of what we need on this iteration
                        freeUp(size - accumulated);
                        // 可以给你分配的内存
                        int got = (int) Math.min(size - accumulated, this.availableMemory);
                        //做内存扣减 13K
                        this.availableMemory -= got;
                        //累加已经分配了多少内存。
                        accumulated += got;
                    }
                }

                // remove the condition for this thread to let the next thread
                // in line start getting memory
                Condition removed = this.waiters.removeFirst();
                if (removed != moreMemory)
                    throw new IllegalStateException("Wrong condition: this shouldn't happen.");

                // signal any additional waiters if there is more memory left
                // over for them
                if (this.availableMemory > 0 || !this.free.isEmpty()) {
    
    
                    if (!this.waiters.isEmpty())
                        this.waiters.peekFirst().signal();
                }

                // unlock and return the buffer
                lock.unlock();
                if (buffer == null)
                    return ByteBuffer.allocate(size);
                else
                    return buffer;
            }
        } finally {
    
    
            if (lock.isHeldByCurrentThread())
                lock.unlock();
        }
    }

deallocate

If the size of the memory you return is equal to the size of a batch, the contents of the memory are cleared, and the memory is put into the memory pool.
If the size of the memory we freed is not the size of a batch, then it will be classified as available memory , Waiting for garbage collection to
release the memory (or after the memory is returned) will wake up the thread waiting for the memory. moreMem.signal() -> moreMemory.await

    public void deallocate(ByteBuffer buffer, int size) {
    
    
        lock.lock();
        try {
    
    
            //如果你还回来的内存的大小 就等于一个批次的大小,
            //我们的参数设置的内存是16K,你计算出来一个批次的大小也是16,申请的内存也是16k
            //16K   32K
            if (size == this.poolableSize && size == buffer.capacity()) {
    
    
                //内存里面的东西清空
                buffer.clear();
                //把内存放入到内存池
                this.free.add(buffer);
            } else {
    
    
                //但是如果 我们释放的内存的大小
                //不是一个批次的大小,那就把归为可用内存
                //等着垃圾回收即可
                this.availableMemory += size;
            }

            Condition moreMem = this.waiters.peekFirst();
            if (moreMem != null)
                //释放了内存(或者是还了内存以后)
                //都会唤醒等待内存的线程。

                //接下来是不是还是要唤醒正在等待分配内存的线程。
                moreMem.signal();
        } finally {
    
    
            lock.unlock();
        }
    }

Guess you like

Origin blog.csdn.net/m0_46449152/article/details/114904449