内存安全的阻塞队列:MemoryLimited/SafeLinkedBlockingQueue

MemoryLimitedLinkedBlockingQueue🚩

这是dubbo源码用到的阻塞队列。继承自 LinkedBlockingQueue。

重写了LinkedBlockingQueue的核心方法,但都是通过MemoryLimiter去做的。

它的目的就是限制队列的内存上限。

// 所有字段如下:
public class MemoryLimiter {
    // 可以粗略的获取要放入阻塞队列的对象的内存大小
    private final Instrumentation inst;
    // 该阻塞队列的总内存限制大小
    private long memoryLimit;
    // 当前使用的内存大小
    private final LongAdder memory;
    // 常规的锁相关,Condition
    private final ReentrantLock acquireLock;
    private final Condition notLimited;
    private final ReentrantLock releaseLock;
    private final Condition notEmpty;
}

核心方法(只放出核心部分):

    public void acquireInterruptibly(Object e) throws InterruptedException {
  			// 加锁
            this.acquireLock.lockInterruptibly();
            try {
                // 利用Instrumentation估算对象内存大小
                long objectSize = this.inst.getObjectSize(e);
 				// 判断内存超限
                while(this.memory.sum() + objectSize >= this.memoryLimit) {
                    this.notLimited.await();
                }
                this.memory.add(objectSize);
                if (this.memory.sum() < this.memoryLimit) {
                    this.notLimited.signal();
                }
            } finally {
                // 解锁
                this.acquireLock.unlock();
            }
            if (this.memory.sum() > 0L) this.signalNotEmpty();   
        }
    }
// 1.获取锁
// 2.如果放入该对象到队列会超出内存限制,阻塞
// 3.内存大小小于内存限制,会尝试唤醒
小结

其实就是保证了队列的总占用内存大小不超过限制。

MemorySafeLinkedBlockingQueue

为了替代 MemoryLimitedLinkedBlockingQueue而生。

它不依赖于Instrumentation获取内存大小。

相反,它从JVM的角度考虑,maxFreeMemory变量代表最大的剩余内存

public class MemorySafeLinkedBlockingQueue<E> extends LinkedBlockingQueue<E> {
    private static final long serialVersionUID = 8032578371739960142L;
    public static int THE_256_MB = 268435456;
    private int maxFreeMemory;
    private Rejector<E> rejector;
那么怎么获取内存信息呢

利用MemoryLimitCalculator类。

public class MemoryLimitCalculator {
    private static volatile long maxAvailable;
    private static final AtomicBoolean refreshStarted = new AtomicBoolean(false);

    public MemoryLimitCalculator() {
    }
}
MemorySafe实现原理

在执行offer/put方法前,都会执行hasRemainedMemory()方法。

而put方法在hasRemainedMemory()return true之后直接调用了父类的方法。

public boolean hasRemainedMemory() {
    return MemoryLimitCalculator.maxAvailable() > (long)this.maxFreeMemory;
}

public void put(final E e) throws InterruptedException {
    if (this.hasRemainedMemory()) {
        // 是直接调用的父类方法,而不像MemoryLimited直接完全重写了
        // 因为它的设计理念是只关心添加元素时候的剩余空间大小,它甚至都不会去关注当前这个元素的大小。
        super.put(e);
    } else {
        this.rejector.reject(e, this);
    }
}

那么核心问题就在于MemoryLimitCalculator.maxAvailable()是怎么实现的

private static void refresh() {
    maxAvailable = Runtime.getRuntime().freeMemory();
}

private static void checkAndScheduleRefresh() {
    if (!refreshStarted.get()) {
        refresh();
        if (refreshStarted.compareAndSet(false, true)) {
            ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Dubbo-Memory-Calculator"));
            scheduledExecutorService.scheduleWithFixedDelay(MemoryLimitCalculator::refresh, 50L, 50L, TimeUnit.MILLISECONDS);
            GlobalResourcesRepository.registerGlobalDisposable(() -> {
                refreshStarted.set(false);
                scheduledExecutorService.shutdown();
            });
        }
    }

}
// MemoryLimitCalculator.maxAvailable()方法
public static long maxAvailable() {
    checkAndScheduleRefresh();
    return maxAvailable;
}
// private static final AtomicBoolean refreshStarted = new AtomicBoolean(false);
// 这个变量用来表示MemoryLimitCalculator是否开始工作
// 如果为false,会先refresh,然后开启线程池每 50ms 运行一次的定时任务
// 定时任务的内容是 触发一下 refresh 方法 保证 maxAvilable 参数的实时性
// 因此对于maxAvailable()方法,如果refreshStarted 为 true,直接返回字段maxAvailable的值
// 否则会先refresh然后定时更新,再返回maxAvailable的值

至此MemoryLimitCalculator源码分析完毕

小结

MemoryLimitCalculator通过线程池定时任务保证我们能够获得JVM剩余空闲内存的大小。

MemorySafeLinkedBlockingQueue只是在真正调用LinkedBlockingQueue的put方法前,判断JVM剩余内存是否足够。判断利用了MemoryLimitCalculator而非Instrumentation。

MemorySafeLinkedBlockingQueue只关心剩余内存够用吗,从全局角度考虑

MemoryLimitedLinkedBlockingQueue只关心队列的内存是否超出限制,专注于队列本身

好文推荐

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值