Netty内存泄露侦测

概览

Netty为了提高性能,自己写了buffer模块,用于存储网络请求的数据。
Netty提供了分配和释放的函数,跟c的malloc和free一样,控制权在用户手里,如果用户用完后忘记调用release函数,会造成该块内存无法被回收,造成内存泄露。
Netty应用WeakReference(新版本修改成PhantomReference),来对内存泄露进行检测。

主要原理

  1. Netty分配ByteBuf后,调用toLeakAwareBuffer方法进行包装,这个过程结束后,ByteBuf的实例会同时由SimpleLeakAwareByteBuf(Bytebuf的包装器)和DefaultResourceLeak(泄露追踪器)持有。
  2. DefaultResourceLeak是WeakReference的实例,所以当SimpleLeakAwareByteBuf被GC清理后,ByteBuf也会被GC清理。但是DefaultResourceLeak可以通过ReferenceQueue得到通知。
  3. DefaultResourceLeak继承至WeakReference,并添加了其持有的ByteBuf的释放标识;SimpleLeakAwareByteBuf的release方法会去清理DefaultResourceLeak的释放标识,从而被ReferenceQueue通知的时候,能够判断是否有内存泄露
    在这里插入图片描述

主要代码片段

PooledByteBufAllocator部分代码

    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        # 代码有删减
        final ByteBuf buf;
        buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
        # 通过SimpleLeakAwareByteBuf包装ByteBuf
        return toLeakAwareBuffer(buf);
    }
    protected static ByteBuf toLeakAwareBuffer(ByteBuf buf) {
        # 代码有删减
        ResourceLeakTracker<ByteBuf> leak;
        # AbstractByteBuf.leakDetector获取泄露监测器,目前只有一种实现:ResourceLeakDetector
        leak = AbstractByteBuf.leakDetector.track(buf);
        # SimpleLeakAwareByteBuf同时持有ByteBufResourceLeakTracker
        buf = new SimpleLeakAwareByteBuf(buf, leak);
        return buf;
    }

SimpleLeakAwareByteBuf部分代码

        @Override
    public boolean release() {
        if (super.release()) {
            closeLeak();
            return true;
        }
        return false;
    }
    private void closeLeak() {
        boolean closed = leak.close(trackedByteBuf);
        assert closed;
    }

ResourceLeakDetector部分代码

    public final ResourceLeakTracker<T> track(T obj) {
        return track0(obj);
    }

    private DefaultResourceLeak track0(T obj) {
    # 代码有删减
        Level level = ResourceLeakDetector.level;
        if (level.ordinal() < Level.PARANOID.ordinal()) {
            # 计算抽样率,符合抽样率后在对内存泄露进行追踪
            if ((PlatformDependent.threadLocalRandom().nextInt(samplingInterval)) == 0) {
                reportLeak();
                return new DefaultResourceLeak(obj, refQueue, allLeaks);
            }
            return null;
        }
        # 最高level会每次都会去检查内存泄露,reportLeak方法就是去检查refQueue
        # 中的DefaultResourceLeak的释放标识
        reportLeak();
        # DefaultResourceLeakWeakReference的之类
        return new DefaultResourceLeak(obj, refQueue, allLeaks);
    }
    private void reportLeak() {
        for (;;) {
            # DefaultResourceLeak持有的ByteBuf被GC后得到通知
            DefaultResourceLeak ref = (DefaultResourceLeak) refQueue.poll();
            if (ref == null) {
                break;
            }
            # 判断DefaultResourceLeak的释放标识,如果释放过说明SimpleLeakAwareByteBuf调用过release
            # 回收了其持有的ByteBuf,否则进行下面的日志打印
            if (!ref.dispose()) {
                continue;
            }
            String records = ref.toString();
            if (reportedLeaks.putIfAbsent(records, Boolean.TRUE) == null) {
                if (records.isEmpty()) {
                    reportUntracedLeak(resourceType);
                } else {
                    reportTracedLeak(resourceType, records);
                }
            }
        }
    }

DefaultResourceLeak extends WeakReference部分代码

        @Override
        public boolean close() {
            if (allLeaks.remove(this, LeakEntry.INSTANCE)) {
                clear();
                headUpdater.set(this, null);
                return true;
            }
            return false;
        }

        @Override
        public boolean close(T trackedObject) {
            assert trackedHash == System.identityHashCode(trackedObject);
            return close() && trackedObject != null;
        }
        
        boolean dispose() {
            clear();
            return allLeaks.remove(this, LeakEntry.INSTANCE);
        }

其他要点

记录调用堆栈

Record extends Throwable
Record是Throwable的子类,DefaultResourceLeak的构造函数中,会创建Record,到时候生内存泄露时,可以通过持有的Record打印创建分配内存时的堆栈信息,辅助定位

思考:通过java的Reference机制能够检测到内存泄漏,为啥Netty不利用这个时机,去回收内存

DefaultResourceLeak是WeakReference的实例,他是弱引用ByteBuf。当通过ReferenceQueue得到通知时,DefaultResourceLeak持有的ByteBuf已经被回收了,不能调用ByteBuf的release了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值